diff --git a/.gitignore b/.gitignore
index 0be0846..6a5c62d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,11 +7,11 @@
/.deprecated
/.notes
/.backup
+/priv
*.ez
erl_crash.dump
*.secret.exs
dev.exs
.env
-mix.lock
todo.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e69de29..388dd6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,133 @@
+# Changelog
+
+
+## v0.4.0
+
+- Bump dependencies and remove many deprecations for elixir 1.5+
+
+## v0.3.6
+
+[changes]
+- Remove `mod: []` as this caused some release warnings. No Module Supervisor
+is started by default.
+- bump version of `:httpipe_adapters_hackney`
+
+## v0.3.5
+
+[changes]
+- Added `:floki` to list of applications to remove
+warnings when making a release.
+
+## v0.3.4
+
+[bug fix]
+- Added `:poison` to list of applications to remove
+warnings when making a release.
+
+## v0.3.3
+
+[bug fix]
+- Fix error `function :hackney.execute_request/5 is undefined or private` when
+running `mix ovh` task by setting the adapter correctly
+
+
+## v0.3.2
+
+[security fix]
+- Remove dependency on `Og`. A potential security issue existed for `og` versions below `1.0.0`. See
+[Code.eval_string/3](https://github.com/elixir-lang/elixir/commit/f1daca5be78e6a466745ba2cdc66d9787c3cf47f#diff-da151e1c1d9b535259a2385407272c9eR107).
+As `Og` was removed anyways as a dependency in `v0.3.2` of `ex_ovh`, this issue is resolved.
+
+[changes]
+- Add default adapter `hackney` to the mix tasks.
+
+[bug fix]
+- Fix setting the query string bug (typo) - `url.encode_query(qs_map)` -> `URI.encode_query(qs_map)`
+- Problem with `Body.apply()` being called in the wrong place
+- Fix `get_prices` in the `Cloud` Request build functions
+
+[enhancements]
+- Add `prepare_request/2` function - prepares the request without sending it. Applies standard transformations.
+
+## v0.3.1
+
+***Security Warning: Versions of `ex_ovh` less than `0.3.2` are deprecated and should not be used
+due to the inclusion of older releases of the dependency `Og` Use versions `0.3.2` or above of `ex_ovh` instead***
+
+[changes]
+- Update some dependencies
+- Relax versioning of some dependencies
+- Include `mix.lock` in version control
+- Add new file `docs/usage.md` with examples on using the api
+- Remove dependency on `morph` - use `Macro.underscore` instead.
+- Reduce depency base - use `:hackney` only
+- remove `Cloud.Cloudstorage` module and place all functions in `Cloud` module ***(breaking change)***
+- change naming of various modules to reduce length ***(breaking change)***
+- change the docs to reflect the move to [httpipe](https://hex.pm/packages/httpipe)
+- with the use of [httpipe](https://hex.pm/packages/httpipe), the abstraction changes from `Query` to `Request` -
+this is essentially a name change only.
+
+[enhancements]
+- Use [httpipe](https://hex.pm/packages/httpipe) and the corresponding hackney adapter.
+- Remove the abstractions based on `Query` and `HTTPQuery` and in it's place use a similar abstraction
+in the third party library [httpipe](https://hex.pm/packages/httpipe)
+
+
+## v0.2
+
+***Security Warning: Versions of `ex_ovh` less than `0.3.2` are deprecated and should not be used
+due to the inclusion of older releases of the dependency `Og` Use versions `0.3.2` or above of `ex_ovh` instead***
+
+[enhancements]
+- Update some dependencies
+- Update mix task so that it will handle activated 2FA on OVH accounts
+- Simplify the readme documentation
+
+[bug fixes]
+- Fix bug in mix task causing it to fail
+- Fix bug in ex_ovh config file where list could not be printed inside "#{}" - causing application to crash
+
+[neutral changes]
+- Merge docs back into the original module files
+
+## v0.1.3
+
+***Security Warning: Versions of `ex_ovh` less than `0.3.2` are deprecated and should not be used
+due to the inclusion of older releases of the dependency `Og` Use versions `0.3.2` or above of `ex_ovh` instead***
+
+- Improve the `mix ovh` docs to better illustrate how to create an application and setup access rules.
+
+## v0.1.2
+
+***Security Warning: Versions of `ex_ovh` less than `0.3.2` are deprecated and should not be used
+due to the inclusion of older releases of the dependency `Og` Use versions `0.3.2` or above of `ex_ovh` instead***
+
+- Fix `mix ovh` task.
+- Fix some of the `/cloud` queries (binary key was missing due to missed earlier change)
+
+
+## v0.1.1
+
+***Security Warning: Versions of `ex_ovh` less than `0.3.2` are deprecated and should not be used
+due to the inclusion of older releases of the dependency `Og` Use versions `0.3.2` or above of `ex_ovh` instead***
+
+- No changes to source code. Only documentation changes.
+
+
+## v0.1.0
+
+***Security Warning: Versions of `ex_ovh` less than `0.3.2` are deprecated and should not be used
+due to the inclusion of older releases of the dependency `Og` Use versions `0.3.2` or above of `ex_ovh` instead***
+
+- Decouple the `Openstack` component into the `Openstex` repository.
+Only requests to the `ExOvh` API can be made with `ExOvh`.
+- Add documentation.
+- Remove no longer used dependency `:secure_random`.
+
+
+## v0.0.1
+
+***Security Warning: Versions of `ex_ovh` less than `0.3.2` are deprecated and should not be used
+due to the inclusion of older releases of the dependency `Og` Use versions `0.3.2` only of `ex_ovh`***
+
+- Initial release.
\ No newline at end of file
diff --git a/LICENCE.md b/LICENCE.md
new file mode 100644
index 0000000..5fa45c0
--- /dev/null
+++ b/LICENCE.md
@@ -0,0 +1,22 @@
+# MIT License
+
+Copyright (c) 2016 - 2017 Stephen Moloney
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
deleted file mode 100644
index b962b24..0000000
--- a/LICENSE.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# MIT License
-
-Copyright (c) 2016 Stephen Moloney
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index f428402..5481dc9 100644
--- a/README.md
+++ b/README.md
@@ -1,71 +1,98 @@
-# ExOvh [](https://travis-ci.org/stephenmoloney/ex_ovh) [](https://hex.pm/packages/ex_ovh) [](https://hexdocs.pm/ex_ovh)
+# ExOvh [](https://travis-ci.org/stephenmoloney/ex_ovh) [](https://hex.pm/packages/ex_ovh) [](https://hexdocs.pm/ex_ovh) [](https://beta.hexfaktor.org/github/stephenmoloney/ex_ovh)
-ExOvh is an helper library in the [elixir language](http://elixir-lang.org/) for the [Ovh Api](https://api.ovh.com/).
+ExOvh is an helper library for the [elixir language](http://elixir-lang.org/) for the [Ovh Api](https://api.ovh.com/).
+To use the Openstack components of the OVH API, see [Openstex](https://github.com/stephenmoloney/openstex)
-## Project Features
+#### Project Features
-- Cache modules (genservers) running in the background which store frequently accessed authentication information.
-- Query and Helper modules for making calls to the OVH API.
-- Query and Helper modules for making calls to the Openstack Swift API. OVH uses openstack for their [webstorage cdn service](https://www.ovh.ie/cdn/webstorage/)
-and their [public cloud storage service](https://www.ovh.ie/cloud/storage/)
+- ***A Supervised agent*** running in the background which stores frequently accessed authentication information.
+- ***Helper*** modules for making building requests to the [Ovh Api](https://api.ovh.com/).
+- ***Request*** functions to send requests to the [Ovh Api](https://api.ovh.com/).
-## Documentation
+#### Getting started - Step 1: Generating the OVH `application key`, `application secret` and `consumer key`.
-- [hex package manager](https://hexdocs.pm/ex_hubic/api-reference.html).
+- This may be done manually by going to `https://eu.api.ovh.com/createApp/` and following the directions outlined by `OVH` at
+[their first steps guide](https://api.ovh.com/g934.first_step_with_api).
-## Getting started
-
-- For setting up just one `ExHubic` client, see [getting started basic](https://hexdocs.pm/ex_hubic/doc/getting_started_basic.md.html).
-- Setting up a custom client or multiple clients, see [getting started advanced](https://hexdocs.pm/ex_hubic/doc/getting_started_advanced.md.html) *(recommended method)*.
+- Alternatively, this may be achieved by running a mix task. This saves me a lot of time when generating a new application.
-## Examples
+- [Documentation here](https://github.com/stephenmoloney/ex_ovh/blob/master/docs/mix_task.md)
-Get account details and containers for given account
-``` ```
+#### Getting Started - Step 2: Generating the OVH client module for your elixir application
-Creating a new container
-``` ```
+- The client module (eg `AwesomeApp.OvhClient`) is the interface for accessing the
+functions of the ***ex_ovh*** `API`.
-Get the container count
-``` ```
+- [Documentation here](https://github.com/stephenmoloney/ex_ovh/blob/master/docs/getting_started.md)
-Adding an object to the "default" container in [OVH CDN Webstorage](https://www.ovh.ie/cdn/webstorage/)
-``` ```
-Listing all objects for "default" container in [OVH CDN Webstorage](https://www.ovh.ie/cdn/webstorage/)
-``` ```
+#### Usage
+#### Examples - Method 1 - Building the queries manually and send the request (my preferred way)
-## Issues, Bug Reports, Feature Requests, Suggestions, Guidance, etc
-- Create [issues here](https://github.com/stephenmoloney/ex_ovh/issues/new) to communicate your ideas to me. Thanks.
+- `GET /me/api/application/#{app_id}`
+```
+app_id = "0"
+req = %HTTPipe.Request{
+ method: :get,
+ url: "/me/api/application/#{app_id}"
+}
+MyApp.OvhClient.request!(req)
+```
+- `GET /cloud/project/{serviceName}/storage`
+```
+service_name = "service_name"
+req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/storage"
+}
+|> MyApp.OvhClient.request!()
+```
+
+
+#### Examples - Method 2 - Build the request using provided helper functions and send the request
+
+***Note:*** Helper functions are only available currently for the `/Cloud` portion of the OVH API.
+*Eventually, I would like to write a macro to create the queries.*
+
+- `GET /cloud/project/{serviceName}/storage`
+```
+ExOvh.V1.Cloud.get_containers(service_name) |> ExOvh.request!()
+```
+
+- For more information [See Hex Docs](https://hexdocs.pm/ex_ovh/0.2/api-reference.html)
+
+
+#### Contributing
-## Contributing
- Pull requests welcome.
-## Tests
+#### Tests
-- Tests against the Swift portion of the library are carried out in the [Openstex library](https://github.com/stephenmoloney/openstex).
-- Tests against the OVH portion of the library have not been written yet.
+- Tests have not been written yet.
-## TODO
+#### TODO
- [ ] Tests for OVH portion of library
- [ ] Option to set the application ttl when running ovh mix task.
-- [ ] Add queries for the remainder of the OVH API. (Webstorage CDN and Cloud are the only ones covered so far)
+- [x] Basic examples to be added to readme of usage of the api.
+- [ ] Add macro for building queries.
+- [ ] Write the usage guide - more examples of using the API.
+- [ ] Remove dependency on `:calendar` if tz info is not required.
-## Note
+#### Note
This is an unofficial client to the OVH api and is not maintained by OVH.
-## Licence
+#### Licence
-MIT
+[MIT Licence](LICENCE.md)
\ No newline at end of file
diff --git a/config/config.exs b/config/config.exs
index 3c44b08..ddf2fe8 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,6 +1,5 @@
use Mix.Config
-
config :logger,
backends: [:console],
level: :debug,
@@ -16,5 +15,4 @@ end
unless Mix.env == :test do
import_config "#{Mix.env}.exs"
-end
-
+end
\ No newline at end of file
diff --git a/config/dev.exs b/config/dev.exs
index 69a7f3d..11a9d6a 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -1,37 +1,19 @@
use Mix.Config
-
config :logger,
backends: [:console],
compile_time_purge_level: :debug
-
-config :openstex,
- httpoison: [
- connect_timeout: 30000, # 30 seconds
- receive_timeout: (60000 * 30) # 30 minutes
- ]
-
-
config :ex_ovh,
ovh: [
application_key: System.get_env("EX_OVH_APPLICATION_KEY"),
application_secret: System.get_env("EX_OVH_APPLICATION_SECRET"),
- consumer_key: System.get_env("EX_OVH_CONSUMER_KEY"),
- endpoint: System.get_env("EX_OVH_ENDPOINT"),
- api_version: System.get_env("EX_OVH_API_VERSION") || "1.0"
+ consumer_key: System.get_env("EX_OVH_CONSUMER_KEY")
],
- swift: [
- webstorage: [
- cdn_name: System.get_env("EX_OVH_WEBSTORAGE_CDN_NAME"),
- type: :webstorage
- ],
- cloudstorage: [
- tenant_id: System.get_env("EX_OVH_CLOUDSTORAGE_TENANT_ID"), # mandatory, corresponds to a project id
- user_id: System.get_env("EX_OVH_CLOUDSTORAGE_USER_ID"), # optional, if absent a user will be created using the ovh api.
- account_temp_url_key: System.get_env("EX_OVH_CLOUDSTORAGE_TEMP_URL_KEY"), # defaults to :nil if absent and won't be added if == :nil.
- keystone_endpoint: "https://auth.cloud.ovh.net/v2.0", # default endpoint for keystone (identity) auth
- region: :nil, # defaults to "SBG1" if set to :nil
- type: :cloudstorage
- ]
- ]
\ No newline at end of file
+ hackney: [
+ connect_timeout: 20000,
+ recv_timeout: 180000
+ ]
+
+config :httpipe,
+ :adapter, HTTPipe.Adapters.Hackney
diff --git a/docs/getting_started.md b/docs/getting_started.md
new file mode 100644
index 0000000..4049dab
--- /dev/null
+++ b/docs/getting_started.md
@@ -0,0 +1,197 @@
+# Getting Started
+
+- This guide assumes your application is called `MyApp`. If your app is called something else, for example `Awesome`,
+then substitute `Awesome` for `MyApp` and `AWESOME_OVH_CLIENT_` for `MY_APP_OVH_CLIENT_` everywhere in this guide.
+
+
+#### Installation
+
+- Add `:ex_ovh` to the dependencies inx `mix.exs`.
+
+```elixir
+defp deps() do
+ [{:ex_ovh, "~> 0.4"}]
+end
+```
+
+#### `OVH` configuration
+
+- Create an OVH account at [OVH](https://www.ovh.com/us/)
+
+- Create an API application at the [OVH API page](https://eu.api.ovh.com/createApp/). Follow the
+ steps outlined by OVH there.
+
+- Add the configuration settings for the OVH application to the project `config.exs`. The following
+environment variables should be set:
+
+ - `MY_APP_OVH_CLIENT_APPLICATION_KEY`
+ - `MY_APP_OVH_CLIENT_APPLICATION_KEY`
+ - `MY_APP_OVH_CLIENT_APPLICATION_KEY`
+
+- Set them by adding them to the `.env` file. It's best not to share `.env` so run
+echo '.env' >> .gitignore` for the git repository to ensure it doesn't get added to source control.
+
+The `.env` file will be similar to as follows:
+
+```
+#!/usr/bin/env bash
+
+# updated on 16.2.2017
+export MY_APP_OVH_CLIENT_APPLICATION_KEY="<application_key>"
+export MY_APP_OVH_CLIENT_APPLICATION_SECRET="<application_secret>"
+export MY_APP_OVH_CLIENT_CONSUMER_KEY="<application_consumer_key>"
+```
+
+- If all of the above seems like too much work, then there is a mix task to help generate the application, see
+[mix task docs](https://github.com/stephenmoloney/ex_ovh/blob/master/docs/mix_task.md).
+
+
+#### Creating a client module
+
+***NOTE:*** Matching naming between `MyApp.OvhClient` and `MY_APP_OVH_CLIENT_APPLICATION_KEY` variables is expected.
+
+- Basic settings
+
+ ```elixir
+ config :my_app, MyApp.OvhClient,
+ ovh: [
+ application_key: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_KEY"),
+ application_secret: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_SECRET"),
+ consumer_key: System.get_env("MY_APP_OVH_CLIENT_CONSUMER_KEY"),
+ ]
+ ```
+
+- More elaborate settings
+
+ ```elixir
+ config :my_app, MyApp.OvhClient,
+ ovh: [
+ application_key: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_KEY"),
+ application_secret: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_SECRET"),
+ consumer_key: System.get_env("MY_APP_OVH_CLIENT_CONSUMER_KEY"),
+ endpoint: "ovh-eu",
+ api_version: "1.0"
+ ],
+ hackney: [ # optional
+ connect_timeout: 20000,
+ recv_timeout: 100000
+ ]
+ ```
+
+#### Starting the `ex_ovh application`
+
+- Add `ex_ovh` to the list of applications so that it is started. Start the `ExOvh` application.
+
+```elixir
+def application do
+ [applications: [:ex_ovh]]
+end
+```
+
+
+#### Create the ovh client module
+
+
+```elixir
+defmodule MyApp.OvhClient do
+ @moduledoc :false
+ use ExOvh.Client, otp_app: :my_app, client: __MODULE__
+end
+```
+
+#### Add the client to the application supervision tree
+
+```elixir
+def start(_type, _args) do
+ import Supervisor.Spec, warn: false
+ spec1 = [supervisor(MyApp.Endpoint, [])]
+ spec2 = [supervisor(MyApp.OvhClient, [])]
+ opts = [strategy: :one_for_one, name: MyApp.Supervisor]
+ Supervisor.start_link(spec1 ++ spec2, opts)
+end
+```
+
+#### Making requests
+
+- The client `MyApp.OvhClient` is now ready.
+
+- To make requests see example usages below:
+
+
+#### Example usages
+
+- First start the application with the system environment variables available `source .env && iex -S mix`
+
+- Then try running some requests against the `API`
+
+
+#### Examples - Method 1 - Building the queries manually and sending the request (my preferred way)
+
+
+- `GET /me`
+
+```
+req = %HTTPipe.Request{
+ method: :get,
+ url: "/me"
+}
+|> MyApp.OvhClient.request!()
+```
+
+- `GET /me/api/application`
+
+```
+req = %HTTPipe.Request{
+ method: :get,
+ url: "/me/api/application"
+}
+|> MyApp.OvhClient.request!()
+```
+
+- `GET /me/api/application/#{app_id}`
+
+```
+app_id = "0"
+req = %HTTPipe.Request{
+ method: :get,
+ url: "/me/api/application/#{app_id}"
+}
+|> MyApp.OvhClient.request!()
+```
+
+- `GET /cloud/project/{serviceName}/storage`
+
+```
+service_name = "service_name"
+req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/storage"
+}
+|> MyApp.OvhClient.request!()
+```
+
+
+#### Examples - Method 2 - Build the request using provided helper functions and sending the request
+
+***Note:*** Helper functions are only available currently for the `/Cloud` portion of the OVH API.
+*Eventually, I would like to write a macro to create the queries.*
+
+- `GET /cloud/project`
+
+```
+ExOvh.V1.Cloud.list_services() |> ExOvh.request!()
+```
+
+- `GET /cloud/project/{serviceName}/storage`
+
+```
+resp1 = ExOvh.V1.Cloud.list_services() |> ExOvh.request!() |> Map.get(:response)
+service = Map.get(resp1, :body) |> List.last()
+resp2 = ExOvh.V1.Cloud.get_containers(service) |> ExOvh.request!() |> Map.get(:response)
+containers = Map.get(resp2, :body)
+```
+
+
+#### Usage guide
+
+- For more usage examples see the usage guide or the [hex documentation](https://hexdocs.pm/ex_ovh/ExOvh.html)
\ No newline at end of file
diff --git a/docs/getting_started_advanced.md b/docs/getting_started_advanced.md
deleted file mode 100644
index 6063aac..0000000
--- a/docs/getting_started_advanced.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# Getting Started (Advanced)
-
-## Installation
-
-- Add `:ex_ovh` to the dependencies.
-
-```elixir
-defp deps() do
- [{:ex_ovh, "~> 0.0.1"}]
-end
-```
-
-- Add the client as a supervisor directly to the supervision tree of your application.
-
-```elixir
-def start(_type, _args) do
- import Supervisor.Spec, warn: false
- spec1 = [supervisor(MyApp.Endpoint, [])]
- spec2 = [supervisor(MyApp.OvhClient, [])]
- opts = [strategy: :one_for_one, name: MyApp.Supervisor]
- Supervisor.start_link(spec1 ++ spec2, opts)
-end
-```
-
-
-## Configuration
-
-*Note:* The configuration assumes that the environment variables such as `MY_APP_OVH_CLIENT_CLIENT_ID` are already created.
-
-- Create an OVH account at [OVH](https://www.ovh.com/us/)
-
-- Create an API application at the [OVH API page](https://eu.api.ovh.com/createApp/). Follow the
- steps outlined by OVH there. Alternatively, there is a [mix task](https://hexdocs.pm/ex_hubic/doc/mix_task_advanced.md.html) which can help
- generate the OVH application.
-
-- Add the configuration settings for the OVH application to your project `config.exs`.
-
-```elixir
-config :my_app, MyApp.OvhClient,
- ovh: [
- application_key: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_KEY"),
- application_secret: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_SECRET"),
- consumer_key: System.get_env("MY_APP_OVH_CLIENT_CONSUMER_KEY"),
- endpoint: System.get_env("MY_APP_OVH_CLIENT_ENDPOINT"),
- api_version: System.get_env("MY_APP_OVH_CLIENT_API_VERSION") || "1.0"
- ]
-```
-
-- Make further configurations if necessary, depending on which OVH services are being used.
-
-- Configuration for [webstorage cdn service](https://www.ovh.ie/cdn/webstorage/)
-
-In the example below, `MY_APP_OVH_CLIENT_WEBSTORAGE_CDN_NAME` is added to the environment variables.
-
-```elixir
-config :ex_ovh,
- ovh: [],
- swift: [
- webstorage: [
- cdn_name: System.get_env("MY_APP_OVH_CLIENT_WEBSTORAGE_CDN_NAME"),
- type: :webstorage
- ]
- ]
-```
-
-- Configuration for public cloud storage service](https://www.ovh.ie/cloud/storage/)
-
-In the example below, `MY_APP_OVH_CLIENT_CLOUDSTORAGE_TENANT_ID` and `MY_APP_OVH_CLIENT_CLOUDSTORAGE_USER_ID` are
-added to the environment variables.
-
-```elixir
-config :ex_ovh,
- ovh: [],
- swift: [
- cloudstorage: [
- tenant_id: System.get_env("MY_APP_OVH_CLIENT_CLOUDSTORAGE_TENANT_ID"), # mandatory, corresponds to a project id
- user_id: System.get_env("MY_APP_OVH_CLIENT_CLOUDSTORAGE_USER_ID"), # optional, if absent a user will be created using the ovh api.
- account_temp_url_key: System.get_env("MY_APP_OVH_CLIENT_CLOUDSTORAGE_TEMP_URL_KEY"), # defaults to :nil if absent and won't be added if == :nil.
- keystone_endpoint: "https://auth.cloud.ovh.net/v2.0", # default endpoint for keystone (identity) auth
- region: :nil, # defaults to "SBG1" if set to :nil
- type: :cloudstorage
- ]
- ]
-```
-
-- Optionally configure `:openstex` which allows customization of [httpoison opts](https://github.com/edgurgel/httpoison/blob/master/lib/httpoison/base.ex#L127)
-for each request. Example configuration for custom [httpoison opts](https://github.com/edgurgel/httpoison/blob/master/lib/httpoison/base.ex#L127) (optional):
-
-```elixir
-config :openstex,
- httpoison: [
- connect_timeout: 30000, # 30 seconds
- receive_timeout: (60000 * 30) # 30 minutes
- ]
-```
\ No newline at end of file
diff --git a/docs/getting_started_basic.md b/docs/getting_started_basic.md
deleted file mode 100644
index a764f8d..0000000
--- a/docs/getting_started_basic.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Getting Started (Basic)
-
-## Installation
-
-- Add `:ex_ovh` to the dependencies.
-
-```elixir
-defp deps() do
- [{:ex_ovh, "~> 0.0.1"}]
-end
-```
-
-- Start `ExOvh` application which in makes `ExOvh` client ready for use.
-
-```elixir
-def application do
- [applications: [:ex_ovh]]
-end
-```
-
-
-## Configuration
-
-*Note:* The configuration assumes that the environment variables such as `EX_OVH_CLIENT_ID` are already created.
-
-- Create an OVH account at [OVH](https://www.ovh.com/us/)
-
-- Create an API application at the [OVH API page](https://eu.api.ovh.com/createApp/). Follow the
- steps outlined by OVH there. Alternatively, there is a [mix task](https://hexdocs.pm/ex_hubic/doc/mix_task_advanced.md.html) which can help
- generate the OVH application.
-
-- Add the configuration settings for the OVH application to your project `config.exs`.
-
-
-```elixir
-config :ex_ovh,
- ovh: [
- application_key: System.get_env("EX_OVH_APPLICATION_KEY"),
- application_secret: System.get_env("EX_OVH_APPLICATION_SECRET"),
- consumer_key: System.get_env("EX_OVH_CONSUMER_KEY"),
- endpoint: System.get_env("EX_OVH_ENDPOINT"),
- api_version: System.get_env("EX_OVH_API_VERSION") || "1.0"
- ]
-```
-
-- Make further configurations if necessary, depending on which OVH services are being used.
-
-- Configuration for [webstorage cdn service](https://www.ovh.ie/cdn/webstorage/)
-
-In the example below, `EX_OVH_WEBSTORAGE_CDN_NAME` is added to the environment variables.
-
-```elixir
-config :ex_ovh,
- ovh: [],
- swift: [
- webstorage: [
- cdn_name: System.get_env("EX_OVH_WEBSTORAGE_CDN_NAME"),
- type: :webstorage
- ]
- ]
-```
-
-- Configuration for public cloud storage service](https://www.ovh.ie/cloud/storage/)
-
-In the example below, `EX_OVH_CLOUDSTORAGE_TENANT_ID` and `EX_OVH_CLOUDSTORAGE_USER_ID` are
-added to the environment variables.
-
-```elixir
-config :ex_ovh,
- ovh: [],
- swift: [
- cloudstorage: [
- tenant_id: System.get_env("EX_OVH_CLOUDSTORAGE_TENANT_ID"), # mandatory, corresponds to a project id
- user_id: System.get_env("EX_OVH_CLOUDSTORAGE_USER_ID"), # optional, if absent a user will be created using the ovh api.
- account_temp_url_key: System.get_env("EX_OVH_CLOUDSTORAGE_TEMP_URL_KEY"), # defaults to :nil if absent and won't be added if == :nil.
- keystone_endpoint: "https://auth.cloud.ovh.net/v2.0", # default endpoint for keystone (identity) auth
- region: :nil, # defaults to "SBG1" if set to :nil
- type: :cloudstorage
- ]
- ]
-```
-
-- Optionally configure `:openstex` which allows customization of [httpoison opts](https://github.com/edgurgel/httpoison/blob/master/lib/httpoison/base.ex#L127)
-for each request. Example configuration for custom [httpoison opts](https://github.com/edgurgel/httpoison/blob/master/lib/httpoison/base.ex#L127) (optional):
-
-```elixir
-config :openstex,
- httpoison: [
- connect_timeout: 30000, # 30 seconds
- receive_timeout: (60000 * 30) # 30 minutes
- ]
-```
\ No newline at end of file
diff --git a/docs/mix_task.md b/docs/mix_task.md
new file mode 100644
index 0000000..cf94673
--- /dev/null
+++ b/docs/mix_task.md
@@ -0,0 +1,125 @@
+# Mix Task
+
+#### Generate ExOvh Config
+
+The ovh mix task makes requests to the OVH API on the user's behalf to generate an OVH application.
+
+- The mix task:
+
+ 1. Creates an application on the user's behalf by sending http requests using the user's username and password credentials.
+ 2. Gets a consumer key and validation url.
+ 3. Validates the validation url on the user's behalf by sending http requests using the user's username and password credentials.
+ 4. Adds the application key, application secret and associated consumer key to the environment configuration.
+
+The environment variables generated by the task are automatically save to a `.env` file. It is best to ensure that this file is outside
+version control.
+
+
+#### Example 1
+
+
+**Shell input:**
+
+```shell
+mix ovh --login=<username-ovh> --password=<password> --appname='my_app_name'
+```
+
+- `login`: username/nic_handle for logging into OVH services. *Note*: must include `-ovh` at the end of the string.
+- `password`: password for logging into OVH services.
+- `appname`: this should correspond to the `otp_app` name in the elixir application. It also doubles as the application name
+for the application in the OVH servers.
+- `redirecturi`: defaults to `""` when absent.
+- `endpoint`: defaults to `"ovh-eu"` wen absent.
+- `accessrules`: defaults to `get-[/*]::put-[/*]::post-[/*]::delete-[/*]` when absent giving the application all
+ full priveleges. One may consider fine-tuning the accessrules, see advanced example below.
+- `appdescription`: defaults to `appname` if absent
+- `clientname`:" This is the elixir client name. defaults to `ExOvh` when the appname is exactly equal to `ex_ovh`, otherwise defaults to `OvhClient`.
+
+
+**Shell Output:**
+
+```elixir
+config :ex_ovh,
+ ovh: [
+ application_key: System.get_env("EX_OVH_APPLICATION_KEY"),
+ application_secret: System.get_env("EX_OVH_APPLICATION_SECRET"),
+ consumer_key: System.get_env("EX_OVH_CONSUMER_KEY"),
+ endpoint: "ovh-eu",
+ api_version: "1.0"
+ ]
+```
+
+Terms explained:
+
+- `EX_OVH_APPLICATION_KEY`: The system environment variable name for the application key.
+- `EX_OVH_APPLICATION_SECRET`: The system environment variable name for the application secret.
+- `EX_OVH_CONSUMER_KEY`: The system environment variable name for the consumer key.
+
+
+#### Example 2
+
+**Shell Input:**
+
+```shell
+mix ovh \
+--login=<username-ovh> \
+--password=<password> \
+--appdescription='Ovh Application for my app' \
+--endpoint='ovh-eu' \
+--apiversion='1.0' \
+--redirecturi='http://localhost:4000/' \
+--accessrules='get-[/*]::put-[/me,/me/*,/cdn/webstorage,/cdn/webstorage/*]::post-[/me,/cdn/webstorage,/cdn/webstorage/*]::delete-[/cdn/webstorage,/cdn/webstorage/*]' \
+--appname='my_app' \
+--clientname='OvhClient'
+```
+
+- `login`: username/nic_handle for logging into OVH services. *Note*: must include `-ovh` at the end of the string.
+- `password`: password for logging into OVH services.
+- `appname`: this should correspond to the `otp_app` name in the elixir application. It also doubles as the application name
+for the application in the OVH servers.
+- `clientname`:" This is the elixir client name. defaults to `ExOvh` when the appname is exactly equal to `ex_ovh`, otherwise defaults to `OvhClient`. `clientname` corresponds to the name of the client. So for example, if appname is `'my_app'` and clientname is
+ `'Client'` then the config file will be `config :my_app, MyApp.Client`. Also, the client in application code can be referred
+ to as `MyApp.Client.function_name`.
+- `appdescription`: A description for the application saved to OVH.
+- `endpoint`: OVH endpoint to be used. May vary depending on the OVH service. See `ExOvh.Defaults`.
+- `apiversion`: version of the api to use. Only one version available currently.
+- `redirecturi`: redirect url for oauth authentication. Should be https.
+- `accessrules`: restrictions can be added to the access rules. In this example, `get` requests to all endpoints are allowed,
+ `put` and `post` requests to `/me` and `/cdn` and `delete` requests are forbidden.
+
+
+**Shell Output:**
+
+
+```elixir
+config :my_app, MyApp.OvhClient,
+ ovh: [
+ application_key: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_KEY"),
+ application_secret: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_SECRET"),
+ consumer_key: System.get_env("MY_APP_OVH_CLIENT_CONSUMER_KEY"),
+ endpoint: "ovh-eu",
+ api_version: "1.0"
+ ]
+```
+
+
+Terms explained:
+
+- `MY_APP_OVH_CLIENT_APPLICATION_KEY`: The system environment variable name for the application key.
+- `MY_APP_OVH_CLIENT_APPLICATION_SECRET`: The system environment variable name for the application secret.
+- `MY_APP_OVH_CLIENT_CONSUMER_KEY`: The system environment variable name for the consumer key.
+
+
+#### Add the settings to the environment
+
+- Copy the configuration outputted by the commandline to `config.exs`.
+
+- The enviroment variables are saved to a file called `.env` automatically by the mix task.
+**Do not add the `.env` file to version control.** It would be prudent to add `.env` to the `.gitignore` file.
+Add the variables to the system environment by running the command or some other commands as appropriate
+to the deployment method.
+
+
+```shell
+source .env
+```
\ No newline at end of file
diff --git a/docs/mix_task_advanced.md b/docs/mix_task_advanced.md
deleted file mode 100644
index 4277b07..0000000
--- a/docs/mix_task_advanced.md
+++ /dev/null
@@ -1,74 +0,0 @@
-# Mix Task (Advanced)
-
-## Generate client config
-
-The hubic mix task makes requests to the Hubic Api on the user's behalf to get details
-such as the referesh token and then generates a configuration which can copied and pasted into `config.exs`.
-
-The environment variables generated by the task are automatically save to a `.env` file. It is best to ensure that this file is outside
-version control.
-
-**Shell Input:**
-
-```shell
-mix ovh \
---login=<username-ovh> \
---password=<password> \
---appdescription='Ovh Application for my app' \
---endpoint='ovh-eu' \
---apiversion='1.0' \
---redirecturi='http://localhost:4000/' \
---accessrules='get-[/*]::put-[/me,/cdn]::post-[/me,/cdn]::delete-[]' \
---appname='my_app'
---clientname='OvhClient'
-```
-
-- `login`: username/nic_handle for logging into OVH services. *Note*: must include `-ovh` at the end of the string.
-- `password`: password for logging into OVH services.
-- `appname`: this should correspond to the `otp_app` name in the elixir application. It also doubles as the application name
-for the application in the OVH servers.
-- `clientname`:" This is the elixir client name. defaults to `ExOvh` when the appname is exactly equal to `ex_ovh`, otherwise defaults to `OvhClient`. `clientname` corresponds to the name of the client. So for example, if appname is `'my_app'` and clientname is
- `'Client'` then the config file will be `config :my_app, MyApp.Client`. Also, the client in application code can be referred
- to as `MyApp.Client.function_name`.
-- `appdescription`: A description for the application saved to OVH.
-- `endpoint`: OVH endpoint to be used. May vary depending on the OVH service. See `ExOvh.Ovh.Defaults`.
-- `apiversion`: version of the api to use. Only one version available currently.
-- `redirecturi`: redirect url for oauth authentication. Should be https.
-- `accessrules`: restrictions can be added to the access rules. In this example, `get` requests to all endpoints are allowed,
- `put` and `post` requests to `/me` and `/cdn` and `delete` requests are forbidden.
-
-
-**Shell Output:**
-
-
-```elixir
-config :my_app, MyApp.OvhClient,
- ovh: [
- application_key: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_KEY"),
- application_secret: System.get_env("MY_APP_OVH_CLIENT_APPLICATION_SECRET"),
- consumer_key: System.get_env("MY_APP_OVH_CLIENT_CONSUMER_KEY"),
- endpoint: System.get_env("MY_APP_OVH_CLIENT_ENDPOINT"),
- api_version: System.get_env("MY_APP_OVH_CLIENT_API_VERSION") || "1.0"
- ]
-```
-
-
-Terms explained:
-
-- `MY_APP_OVH_CLIENT_APPLICATION_KEY`: The system environment variable name for the application key.
-- `MY_APP_OVH_CLIENT_APPLICATION_SECRET`: The system environment variable name for the application secret.
-- `MY_APP_OVH_CLIENT_CONSUMER_KEY`: The system environment variable name for the consumer key.
-- `MY_APP_OVH_CLIENT_ENDPOINT`: The system environment variable name for the ovh endpoint.
-- `MY_APP_OVH_CLIENT_API_VERSION`: The system environment variable name for the api version.
-
-
-- Copy the configuration outputted by the commandline to `config.exs`.
-
-
-- The enviroment variables are saved to a file called `.env` automatically by the mix task.
-**Do not add the `.env` file to version control.** Add the variables to the system environment
-by running the command or some other commands as appropriate to the deployment method.
-
-```shell
-source .env
-```
\ No newline at end of file
diff --git a/docs/mix_task_basic.md b/docs/mix_task_basic.md
deleted file mode 100644
index 8e44964..0000000
--- a/docs/mix_task_basic.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Mix Task (Basic)
-
-## Generate ExOvh Config
-
-The ovh mix task makes requests to the OVH API on the user's behalf to generate an OVH application.
-
-- The mix task:
-
- 1. Creates an application on the user's behalf by sending http requests using the user's username and password credentials.
- 2. Gets a consumer key and validation url.
- 3. Validates the validation url on the user's behalf by sending http requests using the user's username and password credentials.
- 4. Adds the application key, application secret and associated consumer key to the environment configuration.
-
-The environment variables generated by the task are automatically save to a `.env` file. It is best to ensure that this file is outside
-version control.
-
-**Shell input:**
-
-```shell
-mix ovh \
---login=<username-ovh> \
---password=<password> \
---appname='ex_ovh'
-```
-
-- `login`: username/nic_handle for logging into OVH services. *Note*: must include `-ovh` at the end of the string.
-- `password`: password for logging into OVH services.
-- `appname`: this should correspond to the `otp_app` name in the elixir application. It also doubles as the application name
-for the application in the OVH servers.
-- `redirecturi`: defaults to `""` when absent.
-- `endpoint`: defaults to `"ovh-eu"` wen absent.
-- `accessrules`: defaults to `get-[/*]::put-[/*]::post-[/*]::delete-[/*]` when absent giving the application all
- full priveleges. One may consider fine-tuning the accessrules, see advanced example below.
-- `appdescription`: defaults to `appname` if absent
-- `clientname`:" This is the elixir client name. defaults to `ExOvh` when the appname is exactly equal to `ex_ovh`, otherwise defaults to `OvhClient`.
-
-
-**Shell Output:**
-
-```elixir
-config :ex_ovh,
- ovh: [
- application_key: System.get_env("EX_OVH_APPLICATION_KEY"),
- application_secret: System.get_env("EX_OVH_APPLICATION_SECRET"),
- consumer_key: System.get_env("EX_OVH_CONSUMER_KEY"),
- endpoint: System.get_env("EX_OVH_ENDPOINT"),
- api_version: System.get_env("EX_OVH_API_VERSION") || "1.0"
- ]
-```
-
-Terms explained:
-
-- `EX_OVH_APPLICATION_KEY`: The system environment variable name for the application key.
-- `EX_OVH_APPLICATION_SECRET`: The system environment variable name for the application secret.
-- `EX_OVH_CONSUMER_KEY`: The system environment variable name for the consumer key.
-- `EX_OVH_ENDPOINT`: The system environment variable name for the ovh endpoint.
-- `EX_OVH_API_VERSION`: The system environment variable name for the api version.
-
-
-- Copy the configuration outputted by the commandline to `config.exs`.
-
-
-- The enviroment variables are saved to a file called `.env` automatically by the mix task.
-**Do not add the `.env` file to version control.** Add the variables to the system environment
-by running the command or some other commands as appropriate to the deployment method.
-
-
-```shell
-source .env
-```
\ No newline at end of file
diff --git a/docs/usage.md b/docs/usage.md
new file mode 100644
index 0000000..df95b22
--- /dev/null
+++ b/docs/usage.md
@@ -0,0 +1,10 @@
+## Examples using the OVH api
+
+## TODO
+
+
+#### Examples (1) - Building queries manually
+
+
+#### Examples (2) - Building queries with the helper modules
+
diff --git a/lib/application.ex b/lib/application.ex
deleted file mode 100644
index 58c294b..0000000
--- a/lib/application.ex
+++ /dev/null
@@ -1,10 +0,0 @@
-defmodule ExOvh.Application do
- @moduledoc :false
- use Application
-
- # Start the ex_ovh client only if a config :ex_ovh, ex_ovh: %{...} configuration file has been set.
- def start(_type, _args) do
- ExOvh.start_link(__MODULE__)
- end
-
-end
diff --git a/lib/auth/openstack/swift/cache.ex b/lib/auth/openstack/swift/cache.ex
deleted file mode 100644
index bffeb86..0000000
--- a/lib/auth/openstack/swift/cache.ex
+++ /dev/null
@@ -1,213 +0,0 @@
-defmodule ExOvh.Auth.Openstack.Swift.Cache do
- @moduledoc :false
- use GenServer
- use Openstex.Cache
- alias ExOvh.Auth.Openstack.Swift.Cache.Cloudstorage
- alias ExOvh.Auth.Openstack.Swift.Cache.Webstorage
- alias Openstex.Keystone.V2.Helpers.Identity
- import ExOvh.Utils, only: [ets_tablename: 1]
- @get_identity_retries 5
- @get_identity_interval 1000
-
-
- # Public
-
-
- def start_link({ovh_client, swift_client}) do
- Og.context(__ENV__, :debug)
- GenServer.start_link(__MODULE__, {ovh_client, swift_client}, [name: swift_client])
- end
-
- # Pulic Opestex.Cache callbacks (public 'interface' to the Cache module)
-
- def get_swift_public_url(swift_client) do
- get_identity(swift_client)
- |> Map.get(:service_catalog)
- |> Enum.find(fn(%Identity.Service{} = service) -> service.name == "swift" end)
- |> Map.get(:endpoints)
- |> Enum.find(fn(%Identity.Endpoint{} = endpoint) -> endpoint.region == swift_client.config()[:region] end)
- |> Map.get(:public_url)
- end
-
- def get_xauth_token(swift_client) do
- get_identity(swift_client) |> Map.get(:token) |> Map.get(:id)
- end
-
- def get_account_tempurl_key(swift_client) do
- swift_client.config() |> Keyword.fetch!(:account_temp_url_key)
- end
-
- # get_swift_endpoint/1-- use default implementation for `use Openstex.Cache`.
- # get_swift_account/1 -- use default implementation for `use Openstex.Cache`.
-
-
- # Genserver Callbacks
-
-
- # trap exits so that terminate callback is invoked
- # the :lock key is to allow for locking during the brief moment that the access token is being refreshed
- def init({ovh_client, swift_client}) do
- Og.context(__ENV__, :debug)
- :erlang.process_flag(:trap_exit, :true)
- create_ets_table(swift_client)
- config = swift_client.config()
- identity = create_identity({ovh_client, swift_client}, config, config[:type])
- identity = Map.put(identity, :lock, :false)
- :ets.insert(ets_tablename(swift_client), {:identity, identity})
- try_to_set_temp_url_key(swift_client)
- expiry = to_seconds(identity)
- Task.start_link(fn -> monitor_expiry(expiry) end)
- {:ok, {swift_client, identity}}
- end
-
- def handle_call(:add_lock, _from, {swift_client, identity}) do
- Og.context(__ENV__, :debug)
- new_identity = Map.put(identity, :lock, :true)
- :ets.insert(ets_tablename(swift_client), {:identity, new_identity})
- {:reply, :ok, {swift_client, new_identity}}
- end
-
- def handle_call(:remove_lock, _from, {swift_client, identity}) do
- Og.context(__ENV__, :debug)
- new_identity = Map.put(identity, :lock, :false)
- :ets.insert(ets_tablename(swift_client), {:identity, new_identity})
- {:reply, :ok, {swift_client, new_identity}}
- end
-
- def handle_call(:update_identity, _from, {swift_client, identity}) do
- Og.context(__ENV__, :debug)
- {:ok, new_identity} = get_identity(swift_client)
- |> Map.put(identity, :lock, :false)
- :ets.insert(ets_tablename(swift_client), {:identity, new_identity})
- {:reply, :ok, {swift_client, new_identity}}
- end
-
- def handle_call(:stop, _from, state) do
- Og.context(__ENV__, :debug)
- {:stop, :shutdown, :ok, state}
- end
-
- def terminate(:shutdown, {swift_client, _identity}) do
- Og.context(__ENV__, :debug)
- :ets.delete(ets_tablename(swift_client)) # explicilty remove
- :ok
- end
-
-
- # private
-
-
- defp create_identity({ovh_client, swift_client}, config, :webstorage) do
- Og.context(__ENV__, :debug)
- Webstorage.create_identity({ovh_client, swift_client}, config)
- end
- defp create_identity({ovh_client, swift_client}, config, :cloudstorage) do
- Og.context(__ENV__, :debug)
- Cloudstorage.create_identity({ovh_client, swift_client}, config)
- end
- defp create_identity({_ovh_client, _swift_client}, _config, type) do
- Og.context(__ENV__, :debug)
- raise "create_identity/3 is only supported for the :webstorage and :cloudstorage types, #{inspect(type)}"
- end
-
-
- defp try_to_set_temp_url_key(swift_client) do
- if Keyword.has_key?(swift_client.config(), :account_temp_url_key) do
- temp_key = Keyword.get(swift_client.config(), :account_temp_url_key, :nil)
- account = swift_client.account()
- if temp_key != :nil do
- resp = Openstex.Swift.V1.Query.account_info(account) |> swift_client.request!()
- unless Map.has_key?(resp.headers, "X-Account-Meta-Temp-Url-Key") do
- %Openstex.Openstack.Swift.Query{method: :post, uri: account, params: :nil}
- |> swift_client.prepare_request()
- |> Openstex.Utils.put_http_headers(%{"X-Account-Meta-Temp-URL-Key" => temp_key})
- |> swift_client.request!()
- end
- end
- end
- end
-
-
- defp get_identity(swift_client) do
- get_identity(swift_client, 0)
- end
- defp get_identity(swift_client, index) do
- Og.context(__ENV__, :debug)
-
- retry = fn(swift_client, index) ->
- if index > @get_identity_retries do
- raise "Cannot retrieve openstack identity, #{__ENV__.module}, #{__ENV__.line}, client: #{swift_client}"
- else
- :timer.sleep(@get_identity_interval)
- get_identity(swift_client, index + 1)
- end
- end
-
- if ets_tablename(swift_client) in :ets.all() do
- table = :ets.lookup(ets_tablename(swift_client), :identity)
- case table do
- [identity: identity] ->
- if identity.lock === :true do
- retry.(swift_client, index)
- else
- identity
- end
- [] -> retry.(swift_client, index)
- end
- else
- retry.(swift_client, index)
- end
-
- end
-
-
- defp monitor_expiry(expires) do
- Og.context(__ENV__, :debug)
- interval = (expires - 30) * 1000
- :timer.sleep(interval)
- {:reply, :ok, _identity} = GenServer.call(self(), :add_lock)
- {:reply, :ok, _identity} = GenServer.call(self(), :update_identity)
- {:reply, :ok, identity} = GenServer.call(self(), :remove_lock)
- identity |> Og.log_return(__ENV__, :debug)
- expires = to_seconds(identity.token.expires)
- monitor_expiry(expires)
- end
-
-
- defp create_ets_table(swift_client) do
- Og.context(__ENV__, :debug)
- ets_options = [
- :set, # type
- :protected, # read - all, write this process only.
- :named_table,
- {:heir, :none}, # don't let any process inherit the table. when the ets table dies, it dies.
- {:write_concurrency, :false},
- {:read_concurrency, :true}
- ]
- unless ets_tablename(swift_client) in :ets.all() do
- :ets.new(ets_tablename(swift_client), ets_options)
- end
- end
-
-
- defp to_seconds(identity) do
- identity |> Og.log_return(__ENV__, :debug)
- iso_time = identity.token.expires
- {:ok, expiry_ndt, offset} = Calendar.NaiveDateTime.Parse.iso8601(iso_time)
- offset =
- case offset do
- :nil -> 0
- offset -> offset
- end
- {:ok, expiry_dt_utc} = Calendar.NaiveDateTime.with_offset_to_datetime_utc(expiry_ndt, offset)
- {:ok, now} = Calendar.DateTime.from_erl(:calendar.universal_time(), "UTC")
- {:ok, seconds, _microseconds, _when} = Calendar.DateTime.diff(expiry_dt_utc, now)
- if seconds > 0 do
- seconds
- else
- 0
- end
- end
-
-
-end
\ No newline at end of file
diff --git a/lib/auth/openstack/swift/cloudstorage.ex b/lib/auth/openstack/swift/cloudstorage.ex
deleted file mode 100644
index fd846c6..0000000
--- a/lib/auth/openstack/swift/cloudstorage.ex
+++ /dev/null
@@ -1,47 +0,0 @@
-defmodule ExOvh.Auth.Openstack.Swift.Cache.Cloudstorage do
- @moduledoc :false
- alias Openstex.Keystone.V2.Helpers, as: Keystone
- alias Openstex.Keystone.V2.Helpers.Identity
-
-
- @doc :false
- @spec create_identity({atom, atom}, Keyword.t) :: Identity.t | no_return
- def create_identity({ovh_client, _swift_client}, config) do
- Og.context(__ENV__, :debug)
-
- tenant_id = Keyword.fetch!(config, :tenant_id)
- user_id = Keyword.get(config, :user_id, :nil)
-
- user_id =
- case user_id do
- :nil ->
- user = ExOvh.Ovh.V1.Cloud.Query.get_users(tenant_id)
- |> ovh_client.request!()
- |> Map.get(:body)
- |> Enum.find(:nil,
- fn(user) -> %{"description" => "ex_ovh"} = user end
- )
- if user == :nil do
- # create user for "ex_ovh" description
- ExOvh.Ovh.V1.Cloud.Query.create_user(tenant_id, "ex_ovh")
- |> ovh_client.request!()
- |> Map.get("id")
- else
- user["id"]
- end
- user_id -> user_id
- end
-
- resp = ExOvh.Ovh.V1.Cloud.Query.regenerate_credentials(tenant_id, user_id) |> ovh_client.request!()
- password = resp.body["password"]
- username = resp.body["username"]
- endpoint = config[:keystone_endpoint] || "https://auth.cloud.ovh.net/v2.0"
-
- # make sure the regenerate credentials (in the external ovh api) had a chance to take effect
- :timer.sleep(1000)
-
- Keystone.authenticate!(endpoint, username, password, [tenant_id: tenant_id])
- end
-
-
-end
\ No newline at end of file
diff --git a/lib/auth/openstack/swift/webstorage.ex b/lib/auth/openstack/swift/webstorage.ex
deleted file mode 100644
index b2cfb33..0000000
--- a/lib/auth/openstack/swift/webstorage.ex
+++ /dev/null
@@ -1,50 +0,0 @@
-defmodule ExOvh.Auth.Openstack.Swift.Cache.Webstorage do
- @moduledoc :false
- alias Openstex.Keystone.V2.Helpers, as: Keystone
- alias Openstex.Keystone.V2.Helpers.Identity
- defstruct [ :domain, :storage_limit, :server, :endpoint, :username, :password, :tenant_name ]
- @type t :: %__MODULE__{domain: String.t, storage_limit: String.t, server: String.t, endpoint: String.t,
- username: String.t, password: String.t, tenant_name: String.t}
- use ExConstructor
-
-
- @doc :false
- @spec webstorage({atom, atom}, String.t) :: __MODULE__.t | no_return
- def webstorage(ovh_client, cdn_name) do
- Og.context(__ENV__, :debug)
-
- properties = ExOvh.Ovh.V1.Webstorage.Query.get_service(cdn_name) |> ovh_client.request!() |> Map.fetch!(:body)
- |> Og.log_return(__ENV__, :debug)
- credentials = ExOvh.Ovh.V1.Webstorage.Query.get_credentials(cdn_name) |> ovh_client.request!() |> Map.fetch!(:body)
- |> Og.log_return(__ENV__, :debug)
-
- webstorage =
- %{
- "domain" => _domain,
- "storageLimit" => _storage_limit,
- "server" => _server,
- "endpoint" => _endpoint,
- "login" => username,
- "password" => _password,
- "tenant" => tenant_name
- } = Map.merge(properties, credentials)
- webstorage = webstorage
- |> Map.delete("tenant") |> Map.delete("login")
- |> Map.put("username", username) |> Map.put("tenantName", tenant_name)
- __MODULE__.new(webstorage)
- end
-
-
- @doc :false
- @spec create_identity({atom, atom}, Keyword.t) :: Identity.t | no_return
- def create_identity({ovh_client, _swift_client}, config) do
- Og.context(__ENV__, :debug)
-
- cdn_name = Keyword.fetch!(config, :cdn_name)
- webstorage = webstorage(ovh_client, cdn_name)
- %{endpoint: endpoint, username: username, password: password, tenant_name: tenant_name} = webstorage
- Keystone.authenticate!(endpoint, username, password, [tenant_name: tenant_name])
- end
-
-
-end
\ No newline at end of file
diff --git a/lib/auth/ovh/auth.ex b/lib/auth/ovh/auth.ex
deleted file mode 100644
index 957d87e..0000000
--- a/lib/auth/ovh/auth.ex
+++ /dev/null
@@ -1,62 +0,0 @@
-defimpl Openstex.Auth, for: ExOvh.Ovh.Query do
- @moduledoc :false
-
- alias ExOvh.Ovh.Query
- alias ExOvh.Auth.Ovh.Cache
- @default_headers [{"Content-Type", "application/json; charset=utf-8"}]
-
-
- # Public
-
-
- @spec prepare_request(Query.t, Keyword.t, atom) :: Openstex.HttpQuery.t
- def prepare_request(query, httpoison_opts, client)
-
- def prepare_request(%Query{method: method, uri: uri, params: params}, httpoison_opts, client) when method in [:get, :head, :delete] do
- uri = if params !== :nil and params !== "" and is_map(params), do: uri <> "?" <> URI.encode_query(params), else: uri
- uri = if params !== :nil and params !== "" and is_map(params) === :false, do: uri <> URI.encode_www_form(params), else: uri
- ovh_config = client.config()
- uri = ovh_config[:endpoint] <> ovh_config[:api_version] <> uri
- body = params || ""
- headers = headers([ovh_config[:application_secret], ovh_config[:application_key], ovh_config[:consumer_key], Atom.to_string(method), uri, ""], client)
- default_httpoison_opts = client.httpoison_config()
- options = Keyword.merge(default_httpoison_opts, httpoison_opts)
- %Openstex.HttpQuery{method: method, uri: uri, body: body, headers: headers, options: options, service: :ovh}
- end
-
- def prepare_request(%Query{method: method, uri: uri, params: params}, httpoison_opts, client) when method in [:post, :put] do
- if params !== "" and params !== :nil and is_map(params), do: params = Poison.encode!(params)
- ovh_config = client.config()
- uri = ovh_config[:endpoint] <> ovh_config[:api_version] <> uri
- body = params || ""
- headers = headers([ovh_config[:application_secret], ovh_config[:application_key], ovh_config[:consumer_key], Atom.to_string(method), uri, ""], client)
- default_httpoison_opts = client.httpoison_config()
- options = Keyword.merge(default_httpoison_opts, httpoison_opts)
- %Openstex.HttpQuery{method: method, uri: uri, body: body, headers: headers, options: options, service: :ovh}
- end
-
-
- # Private
-
-
- defp headers([app_secret, app_key, consumer_key, method, uri, body] = _opts, client) do
- time = :os.system_time(:seconds) + Cache.get_time_diff(client)
- headers = [
- {"X-Ovh-Application", app_key},
- {"X-Ovh-Consumer", consumer_key},
- {"X-Ovh-Timestamp", time},
- {"X-Ovh-Signature", sign_request([app_secret, consumer_key, String.upcase(method), uri, body, time])}
- ]
- |> Enum.into(%{})
- Map.merge(Enum.into(@default_headers, %{}), headers) |> Enum.into([])
- end
-
-
- defp sign_request([_app_secret, _consumer_key, _method, _uri, _body, _time] = opts) do
- pre_hash = Enum.join(opts, "+")
- post_hash = :crypto.hash(:sha, pre_hash) |> Base.encode16(case: :lower)
- "$1$" <> post_hash
- end
-
-
-end
diff --git a/lib/auth/ovh/cache.ex b/lib/auth/ovh/cache.ex
deleted file mode 100644
index 3e03e99..0000000
--- a/lib/auth/ovh/cache.ex
+++ /dev/null
@@ -1,70 +0,0 @@
-defmodule ExOvh.Auth.Ovh.Cache do
- # Stores the time diff in state of the gen_server - later used in authentication headers for every request
- @moduledoc :false
- use GenServer
-
-
- # Public
-
-
- def start_link(client) do
- Og.context(__ENV__, :debug)
- GenServer.start_link(__MODULE__, client, [name: client])
- end
-
-
- @doc "Retrieves the ovh api time diff from the state"
- def get_time_diff(client) do
- GenServer.call(client, :get_diff)
- end
-
-
- # Genserver Callbacks
-
-
- def init(client) do
- Og.context(__ENV__, :debug)
- diff = calculate_diff(client)
- {:ok, diff}
- end
-
- def handle_call(:get_diff, _from, diff) do
- Og.context(__ENV__, :debug)
- {:reply, diff, diff}
- end
-
- def terminate(:shutdown, _state) do
- Og.context(__ENV__, :warn)
- Og.log_return(__ENV__, "gen_server #{__MODULE__} shutting down", :warn)
- :ok
- end
-
-
- # Private
-
-
- defp calculate_diff(client) do
- Og.context(__ENV__, :debug)
- api_time = api_time_request(client)
- os_t = :os.system_time(:seconds)
- os_t - api_time
- end
-
-
- defp api_time_request(client) do
- Og.context(__ENV__, :debug)
- client |> Og.log_return(__ENV__)
- ovh_config = client.config() |> Og.log_return(__ENV__)
- method = :get
- uri = ovh_config[:endpoint] <> ovh_config[:api_version] <> "/auth/time"
- body = ""
- headers = [{"Content-Type", "application/json; charset=utf-8"}]
- client |> Og.log_return(__ENV__)
- httpoison_opts = client.httpoison_config()
- options = httpoison_opts
- resp = HTTPoison.request!(method, uri, body, headers, options)
- Poison.decode!(resp.body)
- end
-
-
-end
\ No newline at end of file
diff --git a/lib/client.ex b/lib/client.ex
deleted file mode 100644
index e812f87..0000000
--- a/lib/client.ex
+++ /dev/null
@@ -1,189 +0,0 @@
-defmodule ExOvh.Client do
- @moduledoc ~s"""
- A behaviour for setting up an OVH client.
-
- ## Example: Setting up the OVH client
-
- This client is only setup to use the Ovh api. So only the `ExOvh.Ovh` client will be available as the
- configuration settings for anything else are missing.
-
- Defining a client:
-
- defmodule ExOvh do
- @moduledoc :false
- use ExOvh.Client, otp_app: :ex_ovh
- end
-
- Configuring a client:
-
- config :ex_ovh,
- ovh: [
- application_key: System.get_env("EX_OVH_APPLICATION_KEY"),
- application_secret: System.get_env("EX_OVH_APPLICATION_SECRET"),
- consumer_key: System.get_env("EX_OVH_CONSUMER_KEY"),
- endpoint: System.get_env("EX_OVH_ENDPOINT"),
- api_version: System.get_env("EX_OVH_API_VERSION") || "1.0"
- ],
- swift: [
- cloudstorage: [
- tenant_id: System.get_env("EX_OVH_CLOUDSTORAGE_TENANT_ID"), # mandatory, corresponds to a project id
- user_id: System.get_env("EX_OVH_CLOUDSTORAGE_USER_ID"), # optional, if absent a user will be created using the ovh api.
- keystone_endpoint: "https://auth.cloud.ovh.net/v2.0", # default endpoint for keystone (identity) auth
- region: :nil, # defaults to "SBG1" if set to :nil
- type: :cloudstorage
- ]
- ]
-
- ## Example http calls to the OVH api using the `ExOvh.Ovh` client
-
- # Using the Query functions built from scratch
- %ExOvh.Ovh.Query{ method: :get, uri: "/me", params: :nil} |> ExOvh.Ovh.request!() |> Map.get(:body)
- %ExOvh.Ovh.Query{ method: :get, uri: "/cloud/project", params: :nil} |> ExOvh.Ovh.request!() |> Map.get(:body)
-
- # Using the Query functions build using a helper function
- ExOvh.Ovh.V1.Webstorage.Query.get_services() |> ExOvh.Ovh.request!()
-
- ## Example: Setting up the Ovh client and the Swift Cloudstorage client
-
- This client is only setup to use the Ovh api and the cloudstorage swift api. So the `MyApp.MyClient.Ovh` client and the `MyApp.MyClient.Cloudstorage` client
- will be available as the configuration settings for anything else are missing.
-
-
- defmodule MyApp.MyClient do
- @moduledoc :false
- use ExOvh.Client, otp_app: :my_app
- end
-
-
- config :my_app, MyApp.MyClient,
- ovh: [
- application_key: System.get_env("MY_APP_MY_CLIENT_APPLICATION_KEY"),
- application_secret: System.get_env("MY_APP_MY_CLIENT_APPLICATION_SECRET"),
- consumer_key: System.get_env("MY_APP_MY_CLIENT_CONSUMER_KEY"),
- endpoint: System.get_env("MY_APP_MY_CLIENT_ENDPOINT"),
- api_version: System.get_env("MY_APP_MY_CLIENT_API_VERSION") || "1.0"
- ],
- swift: [
- cloudstorage: [
- tenant_id: System.get_env("MY_APP_MY_CLIENT_CLOUDSTORAGE_TENANT_ID"), # mandatory, corresponds to a project id
- user_id: System.get_env("MY_APP_MY_CLIENT_CLOUDSTORAGE_USER_ID"), # optional, if absent a user will be created using the ovh api.
- account_temp_url_key: System.get_env("MY_APP_MY_CLIENT_CLOUDSTORAGE_TEMP_URL_KEY"), # defaults to :nil if absent and won't be added if == :nil.
- keystone_endpoint: "https://auth.cloud.ovh.net/v2.0", # default endpoint for keystone (identity) auth
- region: :nil, # defaults to "SBG1" if set to :nil
- type: :cloudstorage
- ]
- ]
-
- ## Example http calls to a Swift compatible service using the `MyApp.MyClient` client
-
- # Using Query functions
- account = MyApp.MyClient.Swift.Cloudstorage.account()
- container = "ex_ovh_private_test_container"
- Openstex.Swift.V1.Query.get_objects_in_folder("", container, account) |> MyApp.MyClient.Swift.Cloudstorage.request!() |> Map.get(:body)
- Openstex.Swift.V1.Query.container_info(container, account) |> MyApp.MyClient.Swift.Cloudstorage.request!()
- Openstex.Swift.V1.Query.account_info(account) |> MyApp.MyClient.Swift.Cloudstorage.request!()
-
- # Using Helper functions
- MyApp.MyClient.Swift.Cloudstorage.list_objects!(container)
- """
-
- defmacro __using__(opts) do
- quote bind_quoted: [opts: opts] do
- alias ExOvh.Ovh.Defaults
-
- # public functions
-
-
- def config() do
- otp_app = unquote(opts) |> Keyword.fetch!(:otp_app)
- if (otp_app != :ex_ovh) do
- Application.get_env(otp_app, __MODULE__)
- else
- Application.get_all_env(:ex_ovh)
- end
- end
-
-
- def start_link(sup_opts \\ []) do
- ExOvh.Supervisor.start_link(__MODULE__, sup_opts)
- end
-
- # client definitions
-
- defmodule Ovh do
- @moduledoc ExOvh.Client.ovh_docs()
- use Openstex.Client, client: __MODULE__
- def cache(), do: ExOvh.Auth.Ovh.Cache
- @doc :false
- def config() do
- List.last(__ENV__.context_modules).config() |> Keyword.fetch!(:ovh)
- |> Keyword.merge(Defaults.ovh(), fn(k, v1, v2) ->
- case {k, v1} do
- {_, :nil} -> v2
- {:endpoint, v1} -> Defaults.endpoints()[v1]
- _ -> v1
- end
- end)
- end
- end
-
- defmodule Swift.Webstorage do
- @moduledoc ExOvh.Client.swift_docs()
- use Openstex.Client, client: __MODULE__
- use Openstex.Swift.V1.Helpers, client: __MODULE__
-
- @doc :false
- def cache(), do: ExOvh.Auth.Openstack.Swift.Cache
-
- @doc :false
- def config(), do: List.last(__ENV__.context_modules).config() |> Keyword.fetch!(:swift) |> Keyword.fetch!(:webstorage)
-
- @doc "Returns the swift account string."
- @spec account() :: String.t
- def account(), do: __MODULE__.cache().get_swift_account(__MODULE__)
-
- end
-
- defmodule Swift.Cloudstorage do
- @moduledoc ExOvh.Client.swift_docs()
- use Openstex.Client, client: __MODULE__
- use Openstex.Swift.V1.Helpers, client: __MODULE__
-
- @doc :false
- def cache(), do: ExOvh.Auth.Openstack.Swift.Cache
-
- @doc :false
- def config() do
- List.last(__ENV__.context_modules).config() |> Keyword.fetch!(:swift) |> Keyword.fetch!(:cloudstorage)
- |> Keyword.merge(Defaults.cloudstorage(), fn(_k, v1, v2) -> if v1 == :nil, do: v2, else: v1 end)
- end
-
- @doc "Returns the swift account string."
- @spec account() :: String.t
- def account(), do: __MODULE__.cache().get_swift_account(__MODULE__)
-
- end
-
- end
- end
-
- @doc :false
- def ovh_docs() do
- ~s"""
- A default client for sending request to the [OVH API](https://api.ovh.com/console/).
-
- `ExOvh.Ovh` is the default client. `MyApp.MyClient.Ovh` (or similar) can alternatively be the OVH client provided that the client
- has been created by implementing the `ExOvh.Client` behavior.
- """
- end
-
- @doc :false
- def swift_docs() do
- ~s"""
- A default client for sending request to the [Swift Compatible API](http://developer.openstack.org/api-ref-objectstorage-v1.html).
-
- Incorporates the behaviours from the `Openstex.Swift.V1.Helpers` module.
- """
- end
-
-end
\ No newline at end of file
diff --git a/lib/defaults.ex b/lib/defaults.ex
deleted file mode 100644
index cf7f0e6..0000000
--- a/lib/defaults.ex
+++ /dev/null
@@ -1,63 +0,0 @@
-defmodule ExOvh.Ovh.Defaults do
- @moduledoc :false
-
-
- def ovh() do
- [
- endpoint: endpoints()["ovh-eu"],
- api_version: "1.0"
- ]
- end
-
-
- def cloudstorage() do
- [
- keystone_endpoint: "https://auth.cloud.ovh.net/v2.0", # default endpoint for keystone (identity) auth
- region: "SBG1"
- ]
- end
-
-
- def endpoints() do
- %{
- "ovh-eu" => "https://eu.api.ovh.com/",
- "ovh-ca" => "https://ca.api.ovh.com/",
- "kimsufi-eu" => "https://eu.api.kimsufi.com/",
- "kimsufi-ca" => "https://ca.api.kimsufi.com/",
- "soyoustart-eu" => "https://eu.api.soyoustart.com/",
- "soyoustart-ca" => "https://ca.api.soyoustart.com/",
- "runabove-ca" => "https://api.runabove.com/"
- }
- end
-
-
- def create_app_uri_suffix(), do: "createApp/"
-
-
- def consumer_key_suffix(), do: "/auth/credential"
-
-
- def access_rules() do
- [
- %{
- method: "GET",
- path: "/*"
- },
- %{
- method: "POST",
- path: "/*"
- },
- %{
- method: "PUT",
- path: "/*"
- },
- %{
- method: "DELETE",
- path: "/*"
- }
- ]
- end
-
-
-end
-
diff --git a/lib/ex_ovh.ex b/lib/ex_ovh.ex
index 75f9d3b..1121cb9 100644
--- a/lib/ex_ovh.ex
+++ b/lib/ex_ovh.ex
@@ -1,4 +1,3 @@
defmodule ExOvh do
- @moduledoc :false
- use ExOvh.Client, otp_app: :ex_ovh
-end
+ use ExOvh.Client, otp_app: :ex_ovh, client: __MODULE__
+end
\ No newline at end of file
diff --git a/lib/ex_ovh/client.ex b/lib/ex_ovh/client.ex
new file mode 100644
index 0000000..3e010a6
--- /dev/null
+++ b/lib/ex_ovh/client.ex
@@ -0,0 +1,142 @@
+defmodule ExOvh.Client do
+ @moduledoc ~s"""
+ A behaviour for setting up an OVH client.
+
+ ## Example setting up the `ExOvh` Client
+
+ Defining a client:
+
+ defmodule ExOvh do
+ @moduledoc :false
+ use ExOvh.Client, otp_app: :ex_ovh, client: __MODULE__
+ end
+
+ Configuring a client:
+
+ config :ex_ovh,
+ ovh: [
+ application_key: System.get_env("EX_OVH_APPLICATION_KEY"),
+ application_secret: System.get_env("EX_OVH_APPLICATION_SECRET"),
+ consumer_key: System.get_env("EX_OVH_CONSUMER_KEY"),
+ endpoint: "ovh-eu",
+ api_version: "1.0"
+ ],
+ # default hackney options to each request (optional)
+ hackney: [
+ connect_timeout: 20000,
+ recv_timeout: 100000
+ ]
+
+ ## Example using the `ExOvh` client
+
+ %HTTPipe.Request{ method: :get, url: "/me", params: %{}} |> ExOvh.request!()
+ %HTTPipe.Request{ method: :get, url: "/cloud/project", params: %{}} |> ExOvh.request!()
+
+ ## Example (2): Setting up an additional `MyApp.MyClient` client.
+
+ Defining the `MyApp.MyClient`
+
+ defmodule MyApp.MyClient do
+ @moduledoc :false
+ use ExOvh.Client, otp_app: :my_app
+ end
+
+ Configuring the `MyApp.MyClient`
+
+ config :my_app, MyApp.MyClient,
+ ovh: [
+ application_key: System.get_env("MY_APP_MY_CLIENT_APPLICATION_KEY"),
+ application_secret: System.get_env("MY_APP_MY_CLIENT_APPLICATION_SECRET"),
+ consumer_key: System.get_env("MY_APP_MY_CLIENT_CONSUMER_KEY")
+ ],
+ # default hackney options to each request (optional)
+ hackney: [
+ connect_timeout: 20000,
+ recv_timeout: 100000
+ ]
+
+ ## Example using the `MyApp.MyClient` client
+
+ %HTTPipe.Request{ method: :get, url: "/me", params: %{}} |> MyApp.MyClient.request!()
+ %ExOvh.Query{ method: :get, url: "/cloud/project", params: %{}} |> MyApp.MyClient.request!()
+ """
+
+ defmacro __using__(opts) do
+ quote bind_quoted: [opts: opts] do
+
+ @moduledoc ~s"""
+ A default client for sending requests to the [OVH API](https://api.ovh.com/console/).
+
+ `ExOvh` is the default client. Additional clients such as `MyApp.MyClient.Ovh` can be created - see `PAGES`.
+ """
+ alias ExOvh.{Defaults, ResponseError}
+ @behaviour ExOvh.Client
+
+ # public callback functions
+
+ @doc "Starts the client supervision tree"
+ def start_link(sup_opts \\ []) do
+ client = unquote(opts) |> Keyword.fetch!(:client)
+ otp_app = unquote(opts) |> Keyword.fetch!(:otp_app)
+ ExOvh.Supervisor.start_link(client, [otp_app: otp_app])
+ end
+
+ @doc "Gets all the application configuration settings"
+ @spec config() :: Keyword.t
+ def config() do
+ client = unquote(opts) |> Keyword.fetch!(:client)
+ unless agent_exists?(client), do: __MODULE__.start_link([])
+ ExOvh.Config.config(client)
+ end
+
+ @doc "Gets all the `:ovh` configuration settings"
+ @spec ovh_config() :: Keyword.t
+ def ovh_config(), do: config() |> Keyword.fetch!(:ovh)
+
+ @doc "Gets all the default `:hackney` options to be sent with each request"
+ @spec hackney_opts() :: Keyword.t
+ def hackney_opts(), do: config() |> Keyword.fetch!(:hackney)
+
+ @doc "Prepares a request prior to sending by applying standard transformations to the request"
+ @spec prepare_request(HTTPipe.Conn.t | HTTPipe.Request.t) :: HTTPipe.Conn.t
+ def prepare_request(conn) do
+ client = unquote(opts) |> Keyword.fetch!(:client)
+ ExOvh.Request.apply_transformations(conn, client)
+ end
+
+ @doc "Sends a request to the ovh api using [httpipe](https://hex.pm/packages/httpipe)."
+ @spec request(HTTPipe.Conn.t) :: {:ok, HTTPipe.Conn.t} | {:error, HTTPipe.Conn.t}
+ def request(conn) do
+ client = unquote(opts) |> Keyword.fetch!(:client)
+ ExOvh.Request.request(conn, client)
+ end
+
+ @doc "Sends a request to the ovh api using [httpipe](https://hex.pm/packages/httpipe)."
+ @spec request!(HTTPipe.Conn.t) :: HTTPipe.Conn.t | no_return
+ def request!(conn) do
+ case request(conn) do
+ {:ok, conn} -> conn
+ {:error, conn} -> raise(ResponseError, conn: conn)
+ end
+ end
+
+ defp agent_name(client) do
+ Module.concat(ExOvh.Config, client)
+ end
+
+ defp agent_exists?(client) do
+ agent_name(client) in Process.registered()
+ end
+
+ end
+ end
+
+ @callback start_link(sup_opts :: list) :: {:ok, pid} | {:error, atom}
+ @callback config() :: Keyword.t
+ @callback ovh_config() :: Keyword.t
+ @callback hackney_opts() :: Keyword.t
+ @callback prepare_request(HTTPipe.Conn.t | HTTPipe.Request.t) :: HTTPipe.Conn.t
+ @callback request(conn :: HTTPipe.Conn.t) :: {:ok, HTTPipe.Conn.t} | {:error, HTTPipe.Conn.t}
+ @callback request!(conn :: HTTPipe.Conn.t) :: HTTPipe.Conn.t | no_return
+
+end
\ No newline at end of file
diff --git a/lib/ex_ovh/config.ex b/lib/ex_ovh/config.ex
new file mode 100644
index 0000000..abff747
--- /dev/null
+++ b/lib/ex_ovh/config.ex
@@ -0,0 +1,130 @@
+defmodule ExOvh.Config do
+ @moduledoc :false
+ @default_hackney_opts [connect_timeout: 8000, recv_timeout: 5000]
+ alias ExOvh.Defaults
+
+ @doc "Starts an agent for the storage of credentials in memory"
+ def start_agent(client, opts) do
+ otp_app = Keyword.get(opts, :otp_app, :false) || raise("otp_app not specified")
+ Agent.start_link(fn -> config(client, otp_app) end, name: agent_name(client))
+ end
+
+ @doc "Gets all the config.exs environment variables"
+ def get_config_from_env(client, otp_app) do
+ config = Application.get_env(otp_app, client)
+ case otp_app do
+ :ex_ovh -> Application.get_all_env(otp_app)
+ _ ->
+ case config do
+ :nil ->
+ temp_client = Module.split(client) |> List.delete_at(-1) |> Enum.join(".") |> String.to_atom()
+ temp_client = Module.concat(Elixir, temp_client)
+ Application.get_env(otp_app, temp_client)
+ config -> config
+ end
+ end
+ end
+
+ @doc "Gets the hackney config.exs environment variables"
+ def get_ovh_config_from_env(client, otp_app) do
+ try do
+ get_config_from_env(client, otp_app) |> Keyword.fetch!(:ovh)
+ |> Keyword.merge(Defaults.ovh(), fn(k, v1, v2) ->
+ case {k, v1} do
+ {_, :nil} -> v2
+ {:endpoint, v1} -> Defaults.endpoints()[v1]
+ _ -> v1
+ end
+ end)
+ rescue
+ _error -> raise("No ovh_config was found. ")
+ end
+ end
+
+ @doc "Gets the hackney config.exs environment variables"
+ def get_hackney_opts_from_env(client, otp_app) do
+ try do
+ get_config_from_env(client, otp_app) |> Keyword.fetch!(:hackney)
+ rescue
+ _error ->
+ _msg = "No hackney_opts was found. Falling back to default hackney settings #{inspect(@default_hackney_opts)}"
+ @default_hackney_opts
+ end
+ end
+
+ @doc "Gets all the config variables from a supervised Agent"
+ def config(client) do
+ Agent.get(agent_name(client), fn(config) -> config end)
+ end
+
+ @doc "Gets the ovh related config variables from a supervised Agent"
+ def ovh_config(client) do
+ Agent.get(agent_name(client), fn(config) -> config[:ovh] end)
+ end
+
+ @doc "Gets the hackney_opts related config variables from a supervised Agent"
+ def hackney_opts(client) do
+ Agent.get(agent_name(client), fn(config) -> config[:hackney] end)
+ end
+
+ @doc "Gets the diff"
+ def get_diff(client) do
+ Agent.get(agent_name(client), fn(config) -> config[:ovh][:diff] end)
+ end
+
+ defp config(client, otp_app) do
+ diff = calculate_diff(client, otp_app)
+ ovh_config = get_ovh_config_from_env(client, otp_app)
+ |> Keyword.put(:diff, diff)
+
+ hackney_opts = get_hackney_opts_from_env(client, otp_app)
+ connect_timeout = hackney_opts[:connect_timeout] || 30000 # 30 seconds
+ recv_timeout = hackney_opts[:recv_timeout] || (60000 * 30) # 30 minutes
+
+ [
+ ovh: ovh_config,
+ hackney:
+ [
+ timeout: connect_timeout,
+ recv_timeout: recv_timeout,
+ ]
+ ]
+ end
+
+ defp agent_name(client) do
+ Module.concat(__MODULE__, client)
+ end
+
+ defp calculate_diff(client, otp_app) do
+ api_time = api_time_request(client, otp_app)
+ os_t = :os.system_time(:seconds)
+ os_t - api_time
+ end
+
+ defp api_time_request(client, otp_app) do
+ ovh_config = get_ovh_config_from_env(client, otp_app)
+ method = :get
+ url = ovh_config[:endpoint] <> ovh_config[:api_version] <> "/auth/time"
+ body = ""
+ headers = [{"Content-Type", "application/json; charset=utf-8"}]
+ options = get_hackney_opts_from_env(client, otp_app)
+ conn = %HTTPipe.Conn{
+ request: %HTTPipe.Request{
+ method: method,
+ url: url,
+ body: body,
+ headers: headers,
+ },
+ adapter_options: options
+ }
+ {:ok, conn} = HTTPipe.Conn.execute(conn)
+ Poison.decode!(conn.response.body)
+ end
+
+end
+
+
+
+
+
+
diff --git a/lib/ex_ovh/defaults.ex b/lib/ex_ovh/defaults.ex
new file mode 100644
index 0000000..aa67b63
--- /dev/null
+++ b/lib/ex_ovh/defaults.ex
@@ -0,0 +1,55 @@
+defmodule ExOvh.Defaults do
+ @moduledoc :false
+
+
+ def ovh() do
+ [
+ endpoint: endpoints()["ovh-eu"],
+ api_version: "1.0"
+ ]
+ end
+
+
+ def endpoints() do
+ %{
+ "ovh-eu" => "https://eu.api.ovh.com/",
+ "ovh-ca" => "https://ca.api.ovh.com/",
+ "kimsufi-eu" => "https://eu.api.kimsufi.com/",
+ "kimsufi-ca" => "https://ca.api.kimsufi.com/",
+ "soyoustart-eu" => "https://eu.api.soyoustart.com/",
+ "soyoustart-ca" => "https://ca.api.soyoustart.com/",
+ "runabove-ca" => "https://api.runabove.com/"
+ }
+ end
+
+
+ def create_app_uri_suffix(), do: "createApp/"
+
+
+ def consumer_key_suffix(), do: "/auth/credential"
+
+
+ def access_rules() do
+ [
+ %{
+ method: "GET",
+ path: "/*"
+ },
+ %{
+ method: "POST",
+ path: "/*"
+ },
+ %{
+ method: "PUT",
+ path: "/*"
+ },
+ %{
+ method: "DELETE",
+ path: "/*"
+ }
+ ]
+ end
+
+
+end
+
diff --git a/lib/ex_ovh/request.ex b/lib/ex_ovh/request.ex
new file mode 100644
index 0000000..9f4c095
--- /dev/null
+++ b/lib/ex_ovh/request.ex
@@ -0,0 +1,61 @@
+defmodule ExOvh.Request do
+ @moduledoc :false
+
+
+ # Public
+ @spec request(HTTPipe.Conn.t | HTTPipe.Request.t, atom) :: {:ok, HTTPipe.Conn.t} | {:error, HTTPipe.Conn.t}
+ def request(%HTTPipe.Request{} = request, client) do
+ Map.put(HTTPipe.Conn.new(), :request, request)
+ |> request(client)
+ end
+ def request(%HTTPipe.Conn{} = conn, client) do
+ conn = apply_transformations(conn, client)
+
+ case HTTPipe.Conn.execute(conn) do
+ {:ok, conn} ->
+ body = parse_body(conn.response)
+ resp = Map.put(conn.response, :body, body)
+ conn = Map.put(conn, :response, resp)
+ if resp.status_code >= 100 and resp.status_code < 400 do
+ {:ok, conn}
+ else
+ {:error, conn}
+ end
+ {:error, conn} -> {:error, conn}
+ end
+ end
+
+ @doc :false
+ def apply_transformations(conn, client) do
+ conn =
+ unless (:url in Map.get(conn, :completed_transformations, [])) do
+ ExOvh.Transformation.Url.apply(conn, client)
+ else
+ conn
+ end
+ conn =
+ unless (:hackney_options in Map.get(conn, :completed_transformations, [])) do
+ ExOvh.Transformation.HackneyOptions.apply(conn, client)
+ else
+ conn
+ end
+ unless (:auth in Map.get(conn, :completed_transformations, [])) do
+ ExOvh.Transformation.Auth.apply(conn, client)
+ else
+ conn
+ end
+ end
+
+
+ # private
+
+ defp parse_body(resp) do
+ try do
+ resp.body |> Poison.decode!()
+ rescue
+ _ ->
+ resp.body
+ end
+ end
+
+end
diff --git a/lib/ex_ovh/response_error.ex b/lib/ex_ovh/response_error.ex
new file mode 100644
index 0000000..26b5686
--- /dev/null
+++ b/lib/ex_ovh/response_error.ex
@@ -0,0 +1,51 @@
+defmodule ExOvh.ResponseError do
+ @moduledoc :false
+ defexception [:conn]
+
+ def exception([conn: %HTTPipe.Conn{} = conn]) do
+ %ExOvh.ResponseError{conn: conn}
+ end
+
+ def message(%ExOvh.ResponseError{conn: conn}) do
+ ~s"""
+ The following http connection execution was unsuccessful, unexpected or erroneous in some way:
+
+ Request Details:
+ """
+ <>
+ request_output(conn)
+ <>
+ "\nResponse Details:\n"
+ <>
+ response_output(conn)
+ end
+
+ def request_output(conn) do
+ ~s"""
+ ** Request Method **
+ #{Kernel.inspect(conn.request.method)}
+
+ ** Request Body **
+ #{Kernel.inspect conn.request.body}
+
+ ** Request Headers **
+ #{Kernel.inspect(conn.request.headers)}
+
+ ** Request Url **
+ #{Kernel.inspect(conn.request.url)}
+ """
+ end
+
+ def response_output(conn) do
+ ~s"""
+ ** Reponse Status Code **
+ #{Kernel.inspect conn.response.status_code}
+
+ ** Response Body **
+ #{Kernel.inspect(conn.response.body)}
+
+ ** Response Headers **
+ #{Kernel.inspect(conn.response.headers)}
+ """
+ end
+end
\ No newline at end of file
diff --git a/lib/ex_ovh/supervisor.ex b/lib/ex_ovh/supervisor.ex
new file mode 100644
index 0000000..1df4abd
--- /dev/null
+++ b/lib/ex_ovh/supervisor.ex
@@ -0,0 +1,26 @@
+defmodule ExOvh.Supervisor do
+ @moduledoc :false
+ use Supervisor
+
+
+ # Public
+
+
+ def start_link(client, opts \\ []) do
+ Supervisor.start_link(__MODULE__, {client, opts})
+ end
+
+
+ # Callbacks
+
+
+ def init({client, opts}) do
+ sup_tree =
+ [
+ {client, {ExOvh.Config, :start_agent, [client, opts]}, :permanent, 10_000, :worker, [ExOvh.Config]}
+ ]
+ supervise(sup_tree, strategy: :one_for_one, max_restarts: 3, max_seconds: 60) # max restarts 3 in one minute
+ end
+
+
+end
diff --git a/lib/ex_ovh/transformation/auth.ex b/lib/ex_ovh/transformation/auth.ex
new file mode 100644
index 0000000..3787c97
--- /dev/null
+++ b/lib/ex_ovh/transformation/auth.ex
@@ -0,0 +1,41 @@
+defmodule ExOvh.Transformation.Auth do
+ @moduledoc :false
+ @default_headers [{"Content-Type", "application/json; charset=utf-8"}]
+
+
+ # Public
+
+ @spec apply(HTTPipe.Conn.t, atom) :: HTTPipe.Conn.t
+ def apply(%HTTPipe.Conn{request: %HTTPipe.Request{method: method, body: body, headers: headers, url: url}} = conn, client) do
+ trans = Map.get(conn, :completed_transformations, [])
+ unless (Enum.member?(trans, :url)), do: raise ":url must be added to the transformations before applying the :auth step"
+ ovh_config = client.ovh_config()
+ headers = Map.merge(headers, headers([ovh_config[:application_secret], ovh_config[:application_key], ovh_config[:consumer_key], Atom.to_string(method), url, body], client))
+ request = Map.put(conn.request, :headers, headers)
+ Map.put(conn, :request, request)
+ |> Map.put(:completed_transformations, trans ++ [:auth])
+ end
+
+
+ # Private
+
+ defp headers([app_secret, app_key, consumer_key, method, url, body] = _opts, client) do
+ time = :os.system_time(:seconds) + ExOvh.Config.get_diff(client)
+ headers = [
+ {"X-Ovh-Application", app_key},
+ {"X-Ovh-Consumer", consumer_key},
+ {"X-Ovh-Timestamp", time},
+ {"X-Ovh-Signature", sign_request([app_secret, consumer_key, String.upcase(method), url, body, time])}
+ ]
+ |> Enum.into(%{})
+ Map.merge(Enum.into(@default_headers, %{}), headers)
+ end
+
+ defp sign_request([_app_secret, _consumer_key, _method, _uri, _body, _time] = opts) do
+ pre_hash = Enum.join(opts, "+")
+ post_hash = :crypto.hash(:sha, pre_hash) |> Base.encode16(case: :lower)
+ "$1$" <> post_hash
+ end
+
+
+end
diff --git a/lib/ex_ovh/transformation/body.ex b/lib/ex_ovh/transformation/body.ex
new file mode 100644
index 0000000..40bc7a3
--- /dev/null
+++ b/lib/ex_ovh/transformation/body.ex
@@ -0,0 +1,16 @@
+defmodule ExOvh.Transformation.Body do
+ @moduledoc :false
+
+
+ # Public
+
+ @spec apply(HTTPipe.Conn.t, binary) :: HTTPipe.Conn.t
+ def apply(%HTTPipe.Conn{request: %HTTPipe.Request{}} = conn, body) when is_binary(body) do
+ trans = Map.get(conn, :completed_transformations, [])
+ request = Map.put(conn.request, :body, body)
+ Map.put(conn, :request, request)
+ |> Map.put(:completed_transformations, trans ++ [:body])
+ end
+
+
+end
diff --git a/lib/ex_ovh/transformation/hackney_options.ex b/lib/ex_ovh/transformation/hackney_options.ex
new file mode 100644
index 0000000..c2f39a0
--- /dev/null
+++ b/lib/ex_ovh/transformation/hackney_options.ex
@@ -0,0 +1,26 @@
+defmodule ExOvh.Transformation.HackneyOptions do
+ @moduledoc :false
+
+
+ # Public
+
+ @spec apply(HTTPipe.Conn.t, atom) :: HTTPipe.Conn.t
+ def apply(%HTTPipe.Conn{request: %HTTPipe.Request{}} = conn, client) do
+ hackney_options = Map.get(conn, :adapter_options, [])
+ trans = Map.get(conn, :completed_transformations, [])
+ options = merge_options(client.hackney_opts(), hackney_options)
+ Map.put(conn, :adapter_options, options)
+ |> Map.put(:completed_transformations, trans ++ [:hackney_options])
+ end
+
+
+ # Private
+
+ defp merge_options(opts1, opts2) do
+ opts1 = Enum.into(opts1, %{})
+ opts2 = Enum.into(opts2, %{})
+ opts = Map.merge(opts1, opts2)
+ Enum.into(opts, [])
+ end
+
+end
diff --git a/lib/ex_ovh/transformation/url.ex b/lib/ex_ovh/transformation/url.ex
new file mode 100644
index 0000000..4d3ab3c
--- /dev/null
+++ b/lib/ex_ovh/transformation/url.ex
@@ -0,0 +1,58 @@
+defmodule ExOvh.Transformation.Url do
+ @moduledoc :false
+
+
+ # Public
+
+ @spec apply(HTTPipe.Conn.t, atom) :: HTTPipe.Conn.t
+ def apply(%HTTPipe.Conn{request: %HTTPipe.Request{url: url}} = conn, client) do
+ ovh_config = client.ovh_config()
+ trans = Map.get(conn, :completed_transformations, [])
+ url = ovh_config[:endpoint] <> ovh_config[:api_version] <> url
+ request = Map.put(conn.request, :url, url)
+ Map.put(conn, :request, request)
+ |> Map.put(:completed_transformations, trans ++ [:url])
+ end
+
+ @spec apply(HTTPipe.Conn.t, map, atom) :: HTTPipe.Conn.t
+ def apply(%HTTPipe.Conn{} = conn, query_string_map, client) when query_string_map == %{} do
+ __MODULE__.apply(conn, client)
+ end
+ def apply(%HTTPipe.Conn{request: %HTTPipe.Request{url: url}} = conn, query_string_map, client) do
+ ovh_config = client.ovh_config()
+ trans = Map.get(conn, :completed_transformations, [])
+ url = ovh_config[:endpoint] <> ovh_config[:api_version] <> url
+ request = Map.put(conn.request, :url, url)
+ |> add_query_string(query_string_map)
+ Map.put(conn, :request, request)
+ |> Map.put(:completed_transformations, trans ++ [:url])
+ end
+
+
+ @doc ~S"""
+ Add the request string to a request from a map of `name`=`value` elements. Use instead of
+ `ExOvh.Transformation.Url.apply/3` when the `client` is not available but the request should
+ be modified without marking it as fully `transformed` with :url.
+
+ To be used for GET requests.
+
+ ## Example
+
+ service_name="service_name"
+ %HTTPipe.Request{
+ method: :get,
+ url: "/cdn/webstorage/#{service_name}/statistics"
+ }
+ |> ExOvh.Transformation.Url.add_query_string(%{"containerName" => container_name, "region" => region})
+ |> ExOvh.Request!()
+
+ """
+ @spec add_query_string(HTTPipe.Request.t, map) :: HTTPipe.Request.t
+ def add_query_string(%HTTPipe.Request{} = request, qs_map) when qs_map == %{}, do: request
+ def add_query_string(%HTTPipe.Request{url: url} = request, qs_map) do
+ url = url <> "?" <> URI.encode_query(qs_map)
+ Map.put(request, :url, url)
+ end
+
+
+end
diff --git a/lib/ex_ovh/v1/cloud.ex b/lib/ex_ovh/v1/cloud.ex
new file mode 100644
index 0000000..b030dfb
--- /dev/null
+++ b/lib/ex_ovh/v1/cloud.ex
@@ -0,0 +1,976 @@
+defmodule ExOvh.V1.Cloud do
+ @moduledoc ~s"""
+ Helper functions for building requests directed at the cloudstorage related parts of the `/cloud` part of the [OVH API](https://api.ovh.com/console/).
+
+ See `ExOvh.V1.Cloud` for generic cloud requests.
+
+ ## Functions Summary
+
+ | Function | Description | OVH API call |
+ |---|---|---|
+ | `list_services/0` | <small>List available services or list available cloud projects. A returned project id in OVH terms is similar to a tenant id in swift terms</small> | <sub><sup>GET /cloud/project</sup></sub> |
+ | `get_users/1` | <small>Get all users</small> | <sub><sup>GET /cloud/project/{serviceName}/user</sup></sub> |
+ | `create_user/2` | <small>Create user</small> | <sub><sup>POST /ctsloud/project/{serviceName}/user</sup></sub> |
+ | `get_user_details/2` | <small>Get user details. Returns the user_id and username and other details.</small> | <sub><sup>GET /cloud/project/{serviceName}/user/{userId}</sup></sub> |
+ | `delete_user/2` | <small>Delete user</small> | <sub><sup>DELETE /cloud/project/{serviceName}/user/{userId}</sup></sub> |
+ | `download_openrc_script/3` | <small>Get RC file of OpenStack</small> | <sub><sup>GET /cloud/project/{serviceName}/user/{userId}/openrc</sup></sub> |
+ | `regenerate_credentials/2` | <small>Regenerate user credentials including password</small> | <sub><sup>POST /cloud/project/{serviceName}/user/{userId}/regeneratePassword</sup></sub> |
+ | `swift_identity/3` | <small>Gets a json object similar to that returned by Keystone Identity. Includes the 'X-Auth-Token'</small> | <sub><sup>POST /cloud/project/{serviceName}/user/{userId}/token</sup></sub> |
+ | `create_project/2` | <small>Start a new cloud project in the OVH cloud. Corresponds to creating a new Swift stack with a new tenant_id.</small> | <sub><sup>POST /cloud/createProject</sup></sub> |
+ | `get_prices/2` | <small>Get Prices for OVH cloud services.</small> | <sub><sup>GET /cloud/price</sup></sub> |
+ | `project_info/1` | <small>Get information about a project with the project_id (tenant_id)</small> | <sub><sup>GET /cloud/project/{serviceName}</sup></sub> |
+ | `modify_project/2` | <small>Modify a project properties. Change the project description.</small> | <sub><sup>PUT /cloud/project/{serviceName}</sup></sub> |
+ | `project_administrative_info/1` | <small>Get administration information about the project.</small> | <sub><sup>GET /cloud/project/{serviceName}/serviceInfos</sup></sub> |
+ | `project_quotas/1` | <small>Get project quotas.</small> | <sub><sup>GET /cloud/project/{serviceName}/quota</sup></sub> |
+ | `project_regions/1` | <small>Get project regions.</small> | <sub><sup>GET /cloud/project/{serviceName}/region</sup></sub> |
+ | `project_region_info/2` | <small>Get details about a project region.</small> | <sub><sup>GET /cloud/project/{serviceName}/region/{regionName}</sup></sub> |
+ | `project_consumption/3` | <small>Get details about a project consumption for a given `date_from` and `date_to`.</small> | <sub><sup>GET /cloud/project/{serviceName}/consumption</sup></sub> |
+ | `project_bills/3` | <small>Get details about a project billing for a given `date_from` and `date_to`..</small> | <sub><sup>GET /cloud/project/{serviceName}/bill</sup></sub> |
+ | `create_project_alert/4` | <small>Add a new project alert</small> | <sub><sup>POST /cloud/project/{serviceName}/alerting</sup></sub> |
+ | `get_project_alert_info/2` | <small>Get detailed information about a project alert.</small> | <sub><sup>GET /cloud/project/{serviceName}/alerting/{id}</sup></sub> |
+ | `modify_project_alert/5` | <small>Modify an existing project alert.</small> | <sub><sup>PUT /cloud/project/{serviceName}/alerting/{id}</sup></sub> |
+ | `delete_project_alert/2` | <small>Delete an existing project alert.</small> | <sub><sup>DELETE /cloud/project/{serviceName}/alerting/{id}</sup></sub> |
+ | `terminate_service/2` | <small>Terminate a cloud project.</small> | <sub><sup>POST /cloud/project/{serviceName}/terminate</sup></sub> |
+ | `get_containers/1` | <small>Get containers for a given swift tenant id (project id or ovh service name)</small> | <sub><sup>GET /cloud/project/{serviceName}/storage </sup></sub> |
+ | `create_container/3` | <small>Create a container for a given tenant_id (ovh service_name), a container and a region.</small> | <sub><sup>POST /cloud/project/{serviceName}/storage</sup></sub> |
+ | `get_access/1` | <small>Get access details for the Swift API for a given swift tenant_id (ovh service_name)</small> | <sub><sup>GET /cloud/project/{serviceName}/storage/access</sup></sub> |
+ | `container_info/2` | <small>Gets details about a container such as objects, size, region, public or not, static_url, name, ...</small> | <sub><sup>GET /cloud/project/{serviceName}/storage/{containerId}</sup></sub> |
+ | `delete_container/2` | <small>Deletes a given container.</small> | <sub><sup>DELETE /cloud/project/{serviceName}/storage/{containerId}</sup></sub> |
+ | `modify_container_cors/3` | <small>Modify the CORS settings for a container. See [swift docs](http://docs.openstack.org/developer/swift/cors.html)</small> | <sub><sup>POST /cloud/project/{serviceName}/storage/{containerId}/cors Add CORS support on your container</sup></sub> |
+ | `deploy_container_as_static_website/2` | <small>Deploy the container files as a static web site.</small> | <sub><sup>POST /cloud/project/{serviceName}/storage/{containerId}/static</sup></sub> |
+
+ ## Notes
+
+ - `service_name` or `serviceName` corresponds to the Openstack `tenant_id`
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_containers(service_name) |> ExOvh.request!()
+ """
+ alias ExOvh.Transformation.{Body, Url}
+
+
+ @doc ~s"""
+ Get storage containers
+
+ ## Api call
+
+ GET /cloud/project/{serviceName}/storage
+
+ ## Arguments
+
+ - `service_name`: service name for the ovh cloud service
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_containers(service_name) |> ExOvh.request!()
+ """
+ @spec get_containers(String.t) :: HTTPipe.Conn.t
+ def get_containers(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/storage",
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Create container
+
+ ## Api call
+
+ POST /cloud/project/{serviceName}/storage
+
+ ## Arguments
+
+ - `service_name`: service name for the ovh cloud service
+ - `container_name`: name for the new container
+ - `region`: region for the new container, defaults to "SBG1". See regions by running:
+ Currently can choose from "GRA1", "BHS1", "SBG1".
+
+
+ ## Example
+
+ ExOvh.V1.Cloud.create_container(service_name, "test_container") |> ExOvh.request!()
+ """
+ @spec create_container(String.t, String.t, String.t) :: HTTPipe.Conn.t
+ def create_container(service_name, container_name, region \\ "SBG1") do
+ # POST /cloud/project/{serviceName}/storage Create container
+ body =
+ %{
+ "containerName" => container_name,
+ "region" => region
+ }
+ |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/project/#{service_name}/storage"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Gets the x_auth_token and the swift endpoints for a given tenant_id (ovh service_name). A different endpoint is returned
+ depending on the region. Examples of regions include "BHS1", "SBG1", "GRA1". With these details, requests can be made through
+ the Swift api.
+
+ ## Api call
+
+ GET /cloud/project/{serviceName}/storage/access
+
+ ## Arguments
+
+ - `service_name`: service name for the ovh cloud service
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_access(service_name) |> ExOvh.request!()
+ """
+ @spec get_access(String.t) :: HTTPipe.Conn.t
+ def get_access(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/storage/access"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+
+ @doc ~s"""
+ Gets the details for a given container.
+
+ Returns information such as a list of objects in the container, size of the container, whether the container is public
+ or not, the region of the container, the name of the container, the number of stored objects for the container and the
+ static url for the container.
+
+ ## Api call
+
+ GET /cloud/project/{serviceName}/storage/{containerId}
+
+ ## Arguments
+
+ - `service_name`: service name for the ovh cloud service
+ - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
+
+ ## Example
+
+ ExOvh.V1.Cloud.container_info(service_name, container_id) |> ExOvh.request!()
+ """
+ @spec container_info(String.t, String.t) :: HTTPipe.Conn.t
+ def container_info(service_name, container_id) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/storage/#{container_id}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Deletes a given container.
+
+ *Note:* container_d is not the same as container_name.
+
+ ## Api call
+
+ DELETE /cloud/project/{serviceName}/storage/{containerId}
+
+ ## Arguments
+
+ - `service_name`: service name for the ovh cloud service
+ - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
+
+ ## Example
+
+ ExOvh.V1.Cloud.delete_container(service_name, container_id) |> ExOvh.request!()
+ """
+ @spec delete_container(String.t, String.t) :: HTTPipe.Conn.t
+ def delete_container(service_name, container_id) do
+ req = %HTTPipe.Request{
+ method: :delete,
+ url: "/cloud/project/#{service_name}/storage/#{container_id}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Modify CORS settings for a container.
+
+ Modifies the container metadata such that cross origin requests can be permitted.
+ See [CORS section of swift docs](http://docs.openstack.org/developer/swift/cors.html) for more info. Ans see here for more
+ on [CORS in general](http://enable-cors.org/resources.html)
+
+ | Metadata | Use |
+ | --- | --- |
+ | X-Container-Meta-Access-Control-Allow-Origin | Origins to be allowed to make Cross Origin Requests, space separated. |
+
+
+ *Note:* container_d is not the same as container_name.
+
+ ## Api call
+
+ DELETE /cloud/project/{serviceName}/storage/{containerId}
+
+ ## Arguments
+
+ - `service_name`: service name for the ovh cloud service
+ - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
+ - `origin`: an origin that may make cross origin requests to the container. Defaults to `{}` (none) if left absent.
+
+ ## Example
+
+ ExOvh.V1.Cloud.modify_container_cors(service_name, container_id, "http://localhost:4001/") |> ExOvh.request!()
+
+ ## Notes
+
+ To get a full overview of the container details with all metadata, the Swift client should be used. To see the changes, try running the following
+ command for the `container_name` associated with this `container_id`. In fact, the OVH functions are not really required, most changes can be made directly
+ using queries sent via the `Swift.Cloudstorage` client.
+
+ account = ExOvh.Swift.Cloudstorage.account()
+ container = "test_container"
+ Openstex.Swift.V1.container_info(container, account) |> ExOvh.Swift.Cloudstorage.request!() |> Map.get(:headers) |> Map.get("X-Container-Meta-Access-Control-Allow-Origin")
+ """
+ @spec modify_container_cors(String.t, String.t, String.t) :: HTTPipe.Conn.t
+ def modify_container_cors(service_name, container_id, origin \\ {}) do
+ body =
+ %{
+ "origin" => origin
+ }
+ |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/project/#{service_name}/storage/#{container_id}/cors"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+
+ @doc ~s"""
+ Deploy a container as a static website.
+
+ Modifies the ACL settings for a container on the "X-Container-Read" header and also other container metadata.
+ See [swift auth docs](http://docs.openstack.org/developer/swift/overview_auth.html),
+ [swift acl middleware](http://docs.openstack.org/developer/swift/misc.html#module-swift.common.middleware.acl)
+ and [swift account middleware](http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.tempauth)
+ for more information.
+
+ ## Api call
+
+ POST /cloud/project/{serviceName}/storage/{containerId}/static
+
+ ## Arguments
+
+ - `service_name`: service name for the ovh cloud service
+ - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
+
+ ## Example
+
+ ExOvh.V1.Cloud.modify_container_cors(service_name, container_id, "http://localhost:4001/") |> ExOvh.request!()
+
+ ## Notes
+
+ To get a full overview of the container details with all metadata, the Swift client should be used. To see the changes, try running the following
+ command for the `container_name` associated with this `container_id`. In fact, the OVH functions are not really required, most changes can be made directly
+ using queries sent via the `Swift.Cloudstorage` client.
+
+ account = ExOvh.Swift.Cloudstorage.account()
+ container = "test_container"
+ Openstex.Swift.V1.container_info(container, account) |> ExOvh.Swift.Cloudstorage.request!() |> Map.get(:headers)
+ """
+ @spec deploy_container_as_static_website(String.t, String.t) :: HTTPipe.Conn.t
+ def deploy_container_as_static_website(service_name, container_id) do
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/project/#{service_name}/storage/#{container_id}/static"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+
+ @doc ~s"""
+ List available services
+
+ ## Api Call
+
+ GET /cloud/project
+
+ ## Example
+
+ ExOvh.V1.Cloud.list_services() |> ExOvh.request!()
+ """
+ @spec list_services() :: HTTPipe.Conn.t
+ def list_services() do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get all users
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/user
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_users(service_name) |> ExOvh.request!()
+ """
+ @spec get_users(String.t) :: HTTPipe.Conn.t
+ def get_users(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/user"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Create user
+
+ ## Api Call
+
+ POST /cloud/project/{serviceName}/user
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `description`: description ascribed to the new user.
+
+ ## Example
+
+ ExOvh.V1.Cloud.create_user(service_name, "ex_ovh") |> ExOvh.request!()
+ """
+ @spec create_user(String.t, String.t) :: HTTPipe.Conn.t
+ def create_user(service_name, description) do
+ body =
+ %{
+ "description" => description
+ } |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/user"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Get user details. Returns the user_id and username and other details.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/user/{userId}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `user_id`: corresponds to user_id. See `get_users/1`
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_user_details(service_name, user_id) |> ExOvh.request!()
+ """
+ @spec get_user_details(String.t, String.t) :: HTTPipe.Conn.t
+ def get_user_details(service_name, user_id) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/user/#{user_id}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Delete a specific user.
+
+ ## Api Call
+
+ DELETE /cloud/project/{serviceName}/user/{userId}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `user_id`: The user_id. See `get_users/1`
+
+ ## Example
+
+ ExOvh.V1.Cloud.delete_user(service_name, user_id) |> ExOvh.request!()
+ """
+ @spec delete_user(String.t, String.t) :: HTTPipe.Conn.t
+ def delete_user(service_name, user_id) do
+ req = %HTTPipe.Request{
+ method: :delete,
+ url: "/cloud/project/#{service_name}/user/#{user_id}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get RC file of OpenStack. This file is a bash script with much of the openstack credentials. Makes it easier for
+ setting up a swift client from the terminal.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/user/{userId}/openrc
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `user_id`: user_id for user accessing the service.
+ - `region`: region for which the rc file will be created. Defaults to "SBG1" if left absent.
+
+ ## Example
+
+ ExOvh.V1.Cloud.download_openrc_script(service_name, user_id, "SBG1") |> ExOvh.request!()
+ """
+ @spec download_openrc_script(String.t, String.t, String.t) :: HTTPipe.Conn.t
+ def download_openrc_script(service_name, user_id, region \\ "SBG1") do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/user/#{user_id}/openrc",
+ }
+ |> Url.add_query_string(%{region: region})
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Regenerate user password and other credentials.
+
+ ## Api Call
+
+ POST /cloud/project/{serviceName}/user/{userId}/regeneratePassword
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `user_id`: user_id for accessing the project. See `get_users/1`
+
+ ## Example
+
+ ExOvh.V1.Cloud.regenerate_credentials(service_name, user_id) |> ExOvh.request!()
+ """
+ @spec regenerate_credentials(String.t, String.t) :: HTTPipe.Conn.t
+ def regenerate_credentials(service_name, user_id) do
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/project/#{service_name}/user/#{user_id}/regeneratePassword"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get the token for the user (very similar to keystone identity)
+
+ ## Api Call
+
+ POST /cloud/project/{serviceName}/user/{userId}/token
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `user_id`: The swift user_id to login with. See `get_users/1`.
+ - `password`: The swift password to login with. See `regenerate_credentials/2`
+
+ ## Example
+
+ ExOvh.V1.Cloud.swift_identity(service_name, user_id) |> ExOvh.request!()
+ """
+ @spec swift_identity(String.t, String.t, String.t) :: HTTPipe.Conn.t
+ def swift_identity(service_name, user_id, password) do
+ body =
+ %{
+ "password" => password
+ } |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/project/#{service_name}/user/#{user_id}/token"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Create a new Cloud Project.
+
+ ## Api Call
+
+ POST /cloud/createProject
+
+ ## Arguments
+
+ - `description`: project description
+ - `voucher`: ovh voucher code
+
+ ## Example
+
+ ExOvh.V1.Cloud.create_project(description, voucher) |> ExOvh.request!()
+ """
+ @spec create_project(String.t, String.t) :: HTTPipe.Conn.t
+ def create_project(description, voucher) do
+ body =
+ %{
+ "description" => description,
+ "voucher" => voucher
+ } |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/createProject"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Get services prices for the OVH public cloud.
+
+ ## Api Call
+
+ GET /cloud/price
+
+ ## Arguments
+
+ - `region`: prices for a particular region (optional)
+ - `flavor_id`: ovh voucher code (optional)
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_prices() |> ExOvh.request!()
+ """
+ @spec get_prices(String.t | :nil, String.t | :nil) :: HTTPipe.Conn.t
+ def get_prices(region \\ :nil, flavor_id \\ :nil) do
+ params =
+ cond do
+ region == :nil and flavor_id == :nil -> %{}
+ region != :nil and flavor_id == :nil -> %{"region" => region}
+ region == :nil and flavor_id != :nil -> %{"flavorId" => flavor_id}
+ region != :nil and flavor_id != :nil -> %{ "region" => region, "flavorId" => flavor_id }
+ end
+ body = if params == %{}, do: "", else: Poison.encode!(params)
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/price"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Get details for a given project.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.project_info(service_name) |> ExOvh.request!()
+ """
+ @spec project_info(String.t) :: HTTPipe.Conn.t
+ def project_info(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Modify the project description for a project.
+
+ ## Api Call
+
+ PUT /cloud/project/{serviceName}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.modify_project(service_name, new_description) |> ExOvh.request!()
+ """
+ @spec modify_project(String.t, String.t) :: HTTPipe.Conn.t
+ def modify_project(service_name, new_description) do
+ body =
+ %{
+ "description" => new_description
+ } |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :put,
+ url: "/cloud/project/#{service_name}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Get administration information about the project
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/serviceInfos
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.project_administrative_info(service_name) |> ExOvh.request!()
+ """
+ @spec project_administrative_info(String.t) :: HTTPipe.Conn.t
+ def project_administrative_info(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/serviceInfos"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get project quotas.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/quota
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.project_quotas(service_name) |> ExOvh.request!()
+ """
+ @spec project_quotas(String.t) :: HTTPipe.Conn.t
+ def project_quotas(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/quota"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get project regions.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/region
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.project_regions(service_name) |> ExOvh.request!()
+ """
+ @spec project_regions(String.t) :: HTTPipe.Conn.t
+ def project_regions(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/region"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get project details about a project region.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/region/{regionName}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.project_region_info(service_name) |> ExOvh.request!()
+ """
+ @spec project_region_info(String.t, String.t) :: HTTPipe.Conn.t
+ def project_region_info(service_name, region_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/region/#{region_name}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get project details about a project consumption.
+
+ *Note:* Will only allow for up to one month of data to be returned.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/consumption
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `date_from`: starting date in `ISO 8601` format. defaults to 4 weeks/28 days ago (UTC time) if left absent.
+ - `date_to`: end date in `ISO 8601` format. defaults to now (UTC time) if left absent.
+
+ ## Example
+
+ ExOvh.V1.Cloud.project_consumption(service_name) |> ExOvh.request!()
+ """
+ @spec project_consumption(String.t, String.t, String.t) :: HTTPipe.Conn.t
+ def project_consumption(service_name, date_from \\ :nil, date_to \\ :nil) do
+ date_from = if date_from == :nil, do: Calendar.DateTime.now!("Etc/UTC") |> Calendar.DateTime.add!(-(60*60*24*28)) |> Calendar.DateTime.Format.rfc3339(), else: date_from
+ date_to = if date_to == :nil, do: Calendar.DateTime.now!("Etc/UTC") |> Calendar.DateTime.Format.rfc3339(), else: date_to
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/consumption"
+ }
+ |> Url.add_query_string(%{from: date_from, to: date_to})
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get project details about a project bills.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/bill
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `date_from`: starting date in `ISO 8601` format. defaults to 4 weeks/28 days ago (UTC time) if left absent.
+ - `date_to`: end date in `ISO 8601` format. defaults to now (UTC time) if left absent.
+
+ ## Example
+
+ ExOvh.V1.Cloud.project_bills(service_name) |> ExOvh.request!()
+ """
+ @spec project_bills(String.t, String.t, String.t) :: HTTPipe.Conn.t
+ def project_bills(service_name, date_from \\ :nil, date_to \\ :nil) do
+ date_from = if date_from == :nil, do: Calendar.DateTime.now!("Etc/UTC") |> Calendar.DateTime.add!(-(60*60*24*28)) |> Calendar.DateTime.Format.rfc3339(), else: date_from
+ date_to = if date_to == :nil, do: Calendar.DateTime.now!("Etc/UTC") |> Calendar.DateTime.Format.rfc3339(), else: date_to
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/bill"
+ }
+ |> Url.add_query_string(%{from: date_from,to: date_to})
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get a list of project alert ids. These project alert ids can then be looked up in a separate request for more information.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/alerting
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_project_alerts(service_name) |> ExOvh.request!()
+ """
+ @spec get_project_alerts(String.t) :: HTTPipe.Conn.t
+ def get_project_alerts(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/alerting"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Create a new project alert.
+
+ *Notes:*
+ It seems only one alert is allowed per project. To create a new one alter the old one or delete the old one and add a new one.
+ Once the monthly threshold in the given currency is exceeded, then the alert email is sent.
+
+ ## Api Call
+
+ POST /cloud/project/{serviceName}/alerting
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `delay`: The delay between each alert in seconds. This has to be selected from an enumerable (a list). 3600 is the lowest. defaults to 3600. (1 hour)
+ - `email`: The email to send the alert to.
+ - `monthlyThreshold`: The maximum monetary (cash) usage allowed in one month. This is an integer value. Ask OVH about how the currency is chosen.
+
+ ## Example
+
+ ExOvh.V1.Cloud.create_project_alert(service_name, "email_address@email.email", 5) |> ExOvh.request!()
+ """
+ @spec create_project_alert(String.t, String.t, integer, String.t) :: HTTPipe.Conn.t | no_return
+ def create_project_alert(service_name, email, monthly_threshold, delay \\ "3600") do
+ unless is_integer(monthly_threshold), do: raise("monthly_threshold should be an integer!")
+ body =
+ %{
+ "delay" => delay,
+ "email" => email,
+ "monthlyThreshold" => monthly_threshold
+ } |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/project/#{service_name}/alerting"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Get detailed information about a project alert.
+
+ ## Api Call
+
+ GET /cloud/project/{serviceName}/alerting/{id}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `alert_id`: The id of the project alert. See `get_project_alerts/1`
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_project_alert_info(service_name, alert_id) |> ExOvh.request!()
+ """
+ @spec get_project_alert_info(String.t, String.t) :: HTTPipe.Conn.t
+ def get_project_alert_info(service_name, alert_id) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cloud/project/#{service_name}/alerting/#{alert_id}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Modify an existing project alert.
+
+ ## Api Call
+
+ PUT /cloud/project/{serviceName}/alerting/{id}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `alert_id`: The alert to be modified.
+ - `delay`: The delay between each alert in seconds. This has to be selected from an enumerable (a list). 3600 is the lowest. defaults to 3600. (1 hour)
+ - `email`: The email to send the alert to.
+ - `monthlyThreshold`: The maximum monetary (cash) usage allowed in one month. This is an integer value. Ask OVH about how the currency is chosen.
+
+ ## Example
+
+ ExOvh.V1.Cloud.modify_project_alert(service_name, alert_id, "email_address@email.email", 5) |> ExOvh.request!()
+ """
+ @spec modify_project_alert(String.t, String.t, String.t, integer, String.t) :: HTTPipe.Conn.t
+ def modify_project_alert(service_name, alert_id, email, monthly_threshold, delay \\ "3600") do
+ unless is_integer(monthly_threshold), do: raise("monthly_threshold should be an integer!")
+ body = %{
+ "delay" => delay,
+ "email" => email,
+ "monthlyThreshold" => monthly_threshold
+ } |> Poison.encode!()
+ req = %HTTPipe.Request{
+ method: :put,
+ url: "/cloud/project/#{service_name}/alerting/#{alert_id}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ |> Body.apply(body)
+ end
+
+
+ @doc ~s"""
+ Delete a project alert.
+
+ ## Api Call
+
+ DELETE /cloud/project/{serviceName}/alerting/{id}
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+ - `alert_id`: The id of the project alert. See `get_project_alerts/1`
+
+ ## Example
+
+ ExOvh.V1.Cloud.get_project_alert_info(service_name, alert_id) |> ExOvh.request!()
+ """
+ @spec delete_project_alert(String.t, String.t) :: HTTPipe.Conn.t
+ def delete_project_alert(service_name, alert_id) do
+ req = %HTTPipe.Request{
+ method: :delete,
+ url: "/cloud/project/#{service_name}/alerting/#{alert_id}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Terminate a cloud project.
+
+ ## Api Call
+
+ POST /cloud/project/{serviceName}/terminate
+
+ ## Arguments
+
+ - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
+
+ ## Example
+
+ ExOvh.V1.Cloud.HTTPipe.Conn.terminate_project(service_name) |> ExOvh.request!()
+ """
+ @spec terminate_project(String.t) :: HTTPipe.Conn.t
+ def terminate_project(service_name) do
+ req = %HTTPipe.Request{
+ method: :post,
+ url: "/cloud/project/#{service_name}/terminate"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+end
diff --git a/lib/ex_ovh/v1/webstorage.ex b/lib/ex_ovh/v1/webstorage.ex
new file mode 100644
index 0000000..b8fe4ad
--- /dev/null
+++ b/lib/ex_ovh/v1/webstorage.ex
@@ -0,0 +1,174 @@
+defmodule ExOvh.V1.Webstorage do
+ @moduledoc ~s"""
+
+ ***NOTE:*** This is a deprecated service!!!
+
+ Helper functions for building queries directed at the `/cdn/webstorage` part of the [OVH API](https://api.ovh.com/console/).
+
+ ## Functions Summary
+
+ | Function | Description | OVH API call |
+ |---|---|---|
+ | `get_services/0` | <small>Get a list of all webstorage cdn services.</small> | <sub><sup>GET /v1/cdn/webstorage</sup></sub> |
+ | `get_service/1` | <small>Get the domain, server and storage limits for a specific webstorage cdn service</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}</sup></sub> |
+ | `get_service_info/1` | <small>Get a administrative details for a specific webstorage cdn service</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}/serviceInfos</sup></sub> |
+ | `get_service_stats/2` | <small>Get statistics for a specific webstorage cdn service</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}/statistics</sup></sub> |
+ | `get_credentials/1` | <small>Get credentials for using the swift compliant api</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}/statistics</sup></sub> |
+
+
+ ## Example
+
+ ExOvh.V1.Webstorage.get_services() |> ExOvh.request()
+ """
+ alias ExOvh.Transformation.Url
+
+
+
+ @doc ~s"""
+ Get a list of all webstorage cdn services available for the client account
+
+ ## Api call
+
+ GET /v1/cdn/webstorage
+
+ ## Example
+
+ ExOvh.V1.Webstorage.get_services() |> ExOvh.request()
+ """
+ @spec get_services() :: HTTPipe.Conn.t
+ def get_services() do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cdn/webstorage"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+
+ @doc ~s"""
+ Get the domain, server and storage limits for a specific webstorage cdn service
+
+ ## Api call
+
+ GET /v1/cdn/webstorage/{serviceName}
+
+ ## Arguments
+
+ - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
+
+ ## Example
+
+ alias ExOvh.V1.Webstorage
+ service_name = "cdnwebstorage-????"
+ conn = Webstorage.get_service(service_name)
+ {:ok, conn} = ExOvh.Ovh.request(conn)
+ %{
+ "domain" => domain,
+ "storageLimit => storage_limit,
+ "server" => server
+ } = conn.response.body
+ """
+ @spec get_service(String.t) :: HTTPipe.Conn.t
+ def get_service(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cdn/webstorage/#{service_name}"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+
+ @doc ~s"""
+ Get a administrative details for a specific webstorage cdn service
+
+ ## Api call
+
+ GET /v1/cdn/webstorage/{serviceName}/serviceInfos
+
+ ## Arguments
+
+ - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
+
+ ## Example
+
+ alias ExOvh.V1.Webstorage
+ service_name = "cdnwebstorage-????"
+ Webstorage.get_service_info(service_name)
+ {:ok, conn} = ExOvh.Ovh.request(conn)
+ """
+ @spec get_service_info(String.t) :: HTTPipe.Conn.t
+ def get_service_info(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cdn/webstorage/#{service_name}/serviceInfos"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+
+ @doc ~s"""
+ Get statistics for a specific webstorage cdn service
+
+ ## Api call
+
+ GET /v1/cdn/webstorage/{serviceName}/statistics
+
+ ## Arguments
+
+ - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
+ - `options`:
+ - `period can be "month", "week" or "day"`
+ - `type can be "backend", "quota" or "cdn"`
+
+ ## Example
+
+ alias ExOvh.V1.Webstorage
+ service_name = "cdnwebstorage-????"
+ conn = Webstorage.get_service_stats(service_name, [period: "month", type: "backend"])
+ {:ok, conn} = ExOvh.Ovh.request(conn)
+ """
+ @spec get_service_stats(String.t, Keyword.t) :: HTTPipe.Conn.t
+ def get_service_stats(service_name, opts \\ []) do
+ period = Keyword.get(opts, "period", "month")
+ type = Keyword.get(opts, "type", "cdn")
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cdn/webstorage/#{service_name}/statistics"
+ }
+ |> Url.add_query_string(%{"period" => period,"type" => type})
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+ @doc ~s"""
+ Get credentials for using the swift compliant api
+
+ ## Api call
+
+ GET /v1/cdn/webstorage/{serviceName}/credentials
+
+ ## Arguments
+
+ - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
+
+ ## Example
+
+ alias ExOvh.V1.Webstorage
+ service_name = "cdnwebstorage-????"
+ conn = Webstorage.get_webstorage_credentials(service_name)
+ {:ok, conn} = ExOvh.Ovh.request(conn)
+ """
+ @spec get_credentials(String.t) :: HTTPipe.Conn.t
+ def get_credentials(service_name) do
+ req = %HTTPipe.Request{
+ method: :get,
+ url: "/cdn/webstorage/#{service_name}/credentials"
+ }
+ Map.put(HTTPipe.Conn.new(), :request, req)
+ end
+
+
+end
diff --git a/lib/mix/tasks/ovh.ex b/lib/mix/tasks/ovh.ex
index 4660cf4..ed68498 100644
--- a/lib/mix/tasks/ovh.ex
+++ b/lib/mix/tasks/ovh.ex
@@ -1,6 +1,6 @@
defmodule Mix.Tasks.Ovh do
@shortdoc "Create a new application and new credentials for accessing ovh api"
- @moduledoc ~S"""
+ @moduledoc ~s"""
A mix task that generates the ex_ovh application secrets on the user's behalf.
## Steps
@@ -26,19 +26,19 @@ defmodule Mix.Tasks.Ovh do
ovh: [
application_key: System.get_env("EX_OVH_APPLICATION_KEY"),
application_secret: System.get_env("EX_OVH_APPLICATION_SECRET"),
- consumer_key: System.get_env("EX_OVH_CONSUMER_KEY"),
- endpoint: System.get_env("EX_OVH_ENDPOINT"),
- api_version: System.get_env("EX_OVH_API_VERSION") || "1.0"
- ]
+ consumer_key: System.get_env("EX_OVH_CONSUMER_KEY")
+ ]
- See the [mix task basic](https://hexdocs.pm/ex_ovh/doc/mix_task_basic.md.html) or
- [mix task advanced](https://hexdocs.pm/ex_ovh/doc/mix_task_advanced.md.html) for practical steps involved in running the hubic mix task.
+ See the [mix task documentation]((https://github.com/stephenmoloney/ex_ovh/blob/master/docs/mix_task.md).
"""
use Mix.Task
- alias ExOvh.Ovh.Defaults
+ alias ExOvh.Defaults
@default_headers [{"Content-Type", "application/json; charset=utf-8"}]
- @default_options [ timeout: 30000, recv_timeout: (60000 * 1) ]
-
+ @default_adapter HTTPipe.Adapters.Hackney
+ @default_hackney_options [timeout: 30000, recv_timeout: (60000 * 1)]
+ @default_name "ex_ovh"
+ @default_description "ex_ovh application"
+ @default_redirect_uri ""
# Public
@@ -46,23 +46,28 @@ defmodule Mix.Tasks.Ovh do
def run(args) do
opts_map = parse_args(args)
IO.inspect(opts_map, pretty: :true)
+
+ elixir_app_name = Mix.Project.config()[:app]
+
Mix.Shell.IO.info("")
Mix.Shell.IO.info("The details in the map above will be used to create the ovh application.")
Mix.Shell.IO.info("")
+
if Mix.Shell.IO.yes?("Proceed?") do
- HTTPoison.start
+ Application.ensure_all_started(:hackney)
+
opts_map = parse_args(args)
message = get_credentials(opts_map)
|> remove_private()
- |> create_or_update_env_file()
- |> print_config()
+ |> create_or_update_env_file(elixir_app_name)
+ |> print_config(elixir_app_name)
Mix.Shell.IO.info(message)
Mix.Shell.IO.info("")
- Mix.Shell.IO.info("Update the environment variables and all is done here.")
+ Mix.Shell.IO.info("Update the environment variables and you're done''")
Mix.Shell.IO.info("")
- Mix.Shell.IO.info("For example: ")
+ Mix.Shell.IO.info("One way to update the environment variables is to run the command: ")
Mix.Shell.IO.info("")
Mix.Shell.IO.info("source .env")
end
@@ -93,25 +98,25 @@ defmodule Mix.Tasks.Ovh do
defp has_required_args(opts) do
login = Keyword.get(opts, :login, :nil)
- if login === :nil do
+ if login == :nil do
raise "Task requires login argument"
end
password = Keyword.get(opts, :password, :nil)
- if password === :nil do
+ if password == :nil do
raise "Task requires password argument"
end
{opts, %{}}
application_name = Keyword.get(opts, :appname, :ex_ovh)
- if application_name === :nil do
+ if application_name == :nil do
raise "Task requires appname argument"
end
{opts, %{}}
end
- defp parsers_login({opts, acc}), do: {opts, Map.merge(acc, %{login: Keyword.fetch!(opts, :login)}) }
- defp parsers_password({opts, acc}), do: {opts, Map.merge(acc, %{ password: Keyword.fetch!(opts, :password)}) }
- # defp parsers_app_name({opts, acc}), do: {opts, Map.merge(acc, %{ application_name: Keyword.fetch!(opts, :appname)}) }
+ defp parsers_login({opts, acc}), do: {opts, Map.merge(acc, %{login: Keyword.fetch!(opts, :login)})}
+ defp parsers_password({opts, acc}), do: {opts, Map.merge(acc, %{password: Keyword.fetch!(opts, :password)})}
+ # defp parsers_app_name({opts, acc}), do: {opts, Map.merge(acc, %{application_name: Keyword.fetch!(opts, :appname)})}
defp parsers_endpoint({opts, acc}) do
endpoint = Keyword.get(opts, :endpoint, :nil)
endpoint =
@@ -119,7 +124,7 @@ defmodule Mix.Tasks.Ovh do
:nil -> "ovh-eu"
_ -> endpoint
end
- {opts, Map.merge(acc, %{ endpoint: endpoint }) }
+ {opts, Map.merge(acc, %{endpoint: endpoint})}
end
defp parsers_api_version({opts, acc}) do
api_version = Keyword.get(opts, :apiversion, :nil)
@@ -128,38 +133,31 @@ defmodule Mix.Tasks.Ovh do
:nil -> "1.0"
_ -> api_version
end
- {opts, Map.merge(acc, %{ api_version: api_version }) }
+ {opts, Map.merge(acc, %{api_version: api_version})}
end
defp parsers_redirect_uri({opts, acc}) do
- redirect_uri = Keyword.get(opts, :redirecturi, "")
- {opts, Map.merge(acc, %{ redirect_uri: redirect_uri }) }
+ redirect_uri = Keyword.get(opts, :redirecturi, @default_redirect_uri)
+ {opts, Map.merge(acc, %{redirect_uri: redirect_uri})}
end
defp parsers_client_name({opts, acc}) do
client_name = Keyword.get(opts, :clientname, :nil)
- {opts, Map.merge(acc, %{ client_name: client_name }) }
+ {opts, Map.merge(acc, %{client_name: client_name})}
end
defp parsers_app_name({opts, acc}) do
- application_name = Keyword.get(opts, :appname, :nil)
- application_name =
- case application_name do
- :nil -> "ex_ovh"
- _ -> application_name
- end
- {opts, Map.merge(acc, %{ application_name: application_name }) }
+ application_name = Keyword.get(opts, :appname, @default_name)
+ application_name = application_name && application_name || @default_name
+ {opts, Map.merge(acc, %{application_name: application_name})}
end
defp parsers_app_desc({opts, acc}) do
application_description = Keyword.get(opts, :appdescription, :nil)
- application_description =
- case application_description do
- :nil -> "ex_ovh"
- _ -> application_description
- end
- {opts, Map.merge(acc, %{ application_description: application_description }) }
+ application_description = application_description && application_description ||
+ Keyword.get(opts, :appname, @default_description)
+ {opts, Map.merge(acc, %{application_description: application_description})}
end
defp parsers_access_rules({opts, acc}) do
access_rules = Keyword.get(opts, :accessrules, :nil)
access_rules =
- if access_rules === :nil do
+ if access_rules == :nil do
Defaults.access_rules()
else
String.split(access_rules, "::")
@@ -169,73 +167,69 @@ defmodule Mix.Tasks.Ovh do
end)
|> Enum.reduce([], fn({method, concat_paths}, acc) ->
paths = concat_paths
- |> String.lstrip(?[)
- |> String.strip(?]) #rstrip has a bug but fixed in master (01/02/2016)
+ |> String.trim_leading(?[)
+ |> String.trim(?]) #rstrip has a bug but fixed in master (01/02/2016)
|> String.split(",")
- new_rules = Enum.filter_map(paths,
- fn(path) -> path !== "" end,
- fn(path) ->
- %{
- method: String.upcase(method),
- path: path
- }
- end)
+ new_rules = Enum.filter(paths, fn(path) -> path != "" end)
+ |> Enum.map(fn(path) -> %{method: String.upcase(method), path: path} end)
List.insert_at(acc, -1, new_rules)
end)
|> List.flatten()
end
- {opts, Map.merge(acc, %{access_rules: access_rules}) }
+ {opts, Map.merge(acc, %{access_rules: access_rules})}
end
defp get_app_create_page(opts_map) do
- Og.context(__ENV__, :debug)
-
method = :get
- uri = Defaults.endpoints()[opts_map[:endpoint]] <> Defaults.create_app_uri_suffix()
+ url = Defaults.endpoints()[opts_map[:endpoint]] <> Defaults.create_app_uri_suffix()
body = ""
headers = []
- options = @default_options
- resp = HTTPoison.request!(method, uri, body, headers, options)
- Map.get(resp, :body)
+ options = @default_hackney_options
+
+ conn = %HTTPipe.Conn{
+ request: %HTTPipe.Request{
+ method: method,
+ url: url,
+ body: body,
+ headers: headers
+ },
+ adapter: @default_adapter,
+ adapter_options: options
+ }
+ {:ok, conn} = HTTPipe.Conn.execute(conn)
+
+ conn.response.body
end
defp get_create_app_inputs(resp_body) do
- Og.context(__ENV__, :debug)
-
inputs = Floki.find(resp_body, "form input")
|> List.flatten()
- if Enum.any?(inputs, fn(input) -> input === [] end), do: raise "Empty input found"
+ if Enum.any?(inputs, fn(input) -> input == [] end), do: raise "Empty input found"
inputs
end
defp build_app_request(inputs, %{login: login, password: password} = opts_map) do
- Og.context(__ENV__, :debug)
-
{acc, _index, _max} =
Enum.reduce(inputs, {"", 1, Enum.count(inputs)}, fn({"input", input, _}, acc) ->
name = :proplists.get_value("name", input)
- value = ""
+ value =
case name do
- "nic" ->
- value = login
- "password" ->
- value = password
- "applicationName" ->
- value = opts_map.application_name
- "applicationDescription" ->
- value = opts_map.application_description
- _ ->
- raise "Unexpected input"
+ "nic" -> login
+ "password" -> password
+ "applicationName" -> opts_map.application_name
+ "applicationDescription" -> opts_map.application_description
+ _ -> raise "Unexpected input"
end
param = name <> "=" <> value
{acc, index, max} = acc
- if index === max do
- acc = acc <> param
+ acc =
+ if index == max do
+ acc <> param
else
- acc = acc <> param <> "&"
+ acc <> param <> "&"
end
{acc, index + 1, max}
end)
@@ -244,25 +238,33 @@ defmodule Mix.Tasks.Ovh do
defp send_app_request(req_body, opts_map) do
- Og.context(__ENV__, :debug)
-
method = :post
- uri = Defaults.endpoints()[opts_map[:endpoint]] <> Defaults.create_app_uri_suffix()
+ url = Defaults.endpoints()[opts_map[:endpoint]] <> Defaults.create_app_uri_suffix()
body = req_body
headers = [{"Content-Type", "application/x-www-form-urlencoded"}]
- options = @default_options
- resp = HTTPoison.request!(method, uri, body, headers, options)
+ options = @default_hackney_options
+
+ conn = %HTTPipe.Conn{
+ request: %HTTPipe.Request{
+ method: method,
+ url: url,
+ body: body,
+ headers: headers
+ },
+ adapter: @default_adapter,
+ adapter_options: options
+ }
+ {:ok, conn} = HTTPipe.Conn.execute(conn)
+ body = conn.response.body
# Error checking
cond do
- String.contains?(resp.body, msg = "There is already an application with that name for that Account ID") ->
+ String.contains?(body, msg = "There is already an application with that name for that Account ID") ->
raise(msg <> ", try removing the old application first using the ovh api console or just create a new one.")
- String.contains?(resp.body, msg = "Invalid account/password") ->
+ String.contains?(body, msg = "Invalid account/password") ->
raise(msg <> ", try adding '-ovh' to the end of the login")
- String.contains?(resp.body, "Application created") ->
- resp.body
- true ->
- raise "unknown error"
+ String.contains?(body, "Application created") -> body
+ true -> raise "unknown error"
end
end
@@ -292,32 +294,49 @@ defmodule Mix.Tasks.Ovh do
defp get_consumer_key(%{access_rules: access_rules, redirect_uri: redirect_uri} = opts_map) do
- Og.context(__ENV__, :debug)
-
method = :post
- uri = Defaults.endpoints()[opts_map[:endpoint]] <> opts_map[:api_version] <> Defaults.consumer_key_suffix()
- |> Og.log_return(__ENV__)
- body = %{ accessRules: access_rules, redirection: redirect_uri } |> Poison.encode!()
+ url = Defaults.endpoints()[opts_map[:endpoint]] <> opts_map[:api_version] <> Defaults.consumer_key_suffix()
+ body = %{accessRules: access_rules, redirection: redirect_uri} |> Poison.encode!()
headers = Map.merge(Enum.into(@default_headers, %{}), Enum.into([{"X-Ovh-Application", opts_map[:application_key]}], %{})) |> Enum.into([])
- options = @default_options
- resp = HTTPoison.request!(method, uri, body, headers, options)
-
- body = Poison.decode!(Map.get(resp, :body))
+ options = @default_hackney_options
+
+ conn = %HTTPipe.Conn{
+ request: %HTTPipe.Request{
+ method: method,
+ url: url,
+ body: body,
+ headers: headers
+ },
+ adapter: @default_adapter,
+ adapter_options: options
+ }
+ {:ok, conn} = HTTPipe.Conn.execute(conn)
+
+ body = Poison.decode!(conn.response.body)
{Map.get(body, "consumerKey"), Map.get(body, "validationUrl")}
end
defp bind_consumer_key_to_app({ck, validation_url}, opts_map) do
- Og.context(__ENV__, :debug)
-
method = :get
- uri = validation_url
+ url = validation_url
body = ""
headers = []
- options = @default_options
- resp = HTTPoison.request!(method, uri, body, headers, options)
-
- Map.get(resp, :body)
+ options = @default_hackney_options
+
+ conn = %HTTPipe.Conn{
+ request: %HTTPipe.Request{
+ method: method,
+ url: url,
+ body: body,
+ headers: headers
+ },
+ adapter: @default_adapter,
+ adapter_options: options
+ }
+ {:ok, conn} = HTTPipe.Conn.execute(conn)
+
+ conn.response.body
|> get_bind_ck_to_app_inputs()
|> build_ck_binding_request(opts_map)
|> send_ck_binding_request(validation_url, ck)
@@ -325,27 +344,22 @@ defmodule Mix.Tasks.Ovh do
defp get_bind_ck_to_app_inputs(resp_body) do
- Og.context(__ENV__, :debug)
-
inputs = Floki.find(resp_body, "form input") ++
Floki.find(resp_body, "form select")
|> List.flatten()
|> Enum.filter(fn({_type, input, _options}) ->
- :proplists.get_value("name", input) !== "identifiant"
+ :proplists.get_value("name", input) != "identifiant"
end)
- if Enum.any?(inputs, fn(input) -> input === [] end), do: raise "Inputs should not be empty"
+ if Enum.any?(inputs, fn(input) -> input == [] end), do: raise "Inputs should not be empty"
inputs
end
defp build_ck_binding_request(inputs, %{login: login, password: password} = _opts_map) do
- Og.context(__ENV__, :debug)
-
- {acc, _index, _max} =
- Enum.reduce(inputs, {"", 1, Enum.count(inputs)}, fn({type, input, _options}, acc) ->
+ Enum.reduce(inputs, "", fn({type, input, _options}, acc) ->
{name_val, value} =
- cond do
- type == "input" && {"name", "credentialToken"} in input ->
+ cond do
+ type == "input" && {"name", "credentialToken"} in input ->
name_val = :proplists.get_value("name", input)
value = :proplists.get_value("value", input)
{name_val, value}
@@ -353,7 +367,7 @@ defmodule Mix.Tasks.Ovh do
name_val = :proplists.get_value("name", input)
value = password
{name_val, value}
- type == "input" && {"type", "text"} in input && {"placeholder", "Account ID"} in input ->
+ type == "input" && {"type", "text"} in input && {"placeholder", "Account ID or email address"} in input ->
name_val = :proplists.get_value("name", input)
value = login
{name_val, value}
@@ -362,65 +376,137 @@ defmodule Mix.Tasks.Ovh do
value = "0"
{name_val, value}
true ->
- raise "Unexpected input"
+ # raise "Unexpected input"
+ {:no_name, :no_val}
end
- param = name_val <> "=" <> value
- {acc, index, max} = acc
- if index === max do
- acc = acc <> param
- else
- acc = acc <> param <> "&"
+ case {name_val, value} do
+ {:no_name, :no_val} -> acc
+ {name_val, value} -> acc <> name_val <> "=" <> value <> "&"
end
- {acc, index + 1, max}
end)
- acc
+ |> String.trim_trailing("&")
end
defp send_ck_binding_request(req_body, validation_url, ck) do
- Og.context(__ENV__, :debug)
-
method = :post
- uri = validation_url
+ url = validation_url
body = req_body
headers = [{"Content-Type", "application/x-www-form-urlencoded"}]
- options = @default_options
- resp = HTTPoison.request!(method, uri, body, headers, options)
-
- resp |> Og.log_return(__ENV__)
+ options = @default_hackney_options
+
+ conn = %HTTPipe.Conn{
+ request: %HTTPipe.Request{
+ method: method,
+ url: url,
+ body: body,
+ headers: headers
+ },
+ adapter: @default_adapter,
+ adapter_options: options
+ }
+ {:ok, conn} = HTTPipe.Conn.execute(conn)
+
+ case check_for_successful_binding(conn.response, validation_url, ck) do
+ {:ok, :handle_2fa} -> handle_2fa(conn.response.body, validation_url, ck)
+ {:ok, ck} -> ck
+ {:error, msg} -> raise msg
+ end
+ end
+ defp check_for_successful_binding(resp, validation_url, ck) do
error_msg1 = "Failed to bind the consumer token to the application. Please try to validate the consumer token manually at #{validation_url}"
error_msg2 = "Invalid validity period entered for the consumer token. Please try to validate the consumer token manually at #{validation_url}"
cond do
- String.contains?(resp.body, "Invalid validity") ->
- raise error_msg2
- String.contains?(resp.body, "The token is now valid, it can be used in the application") ->
- ck
- String.contains?(resp.body, "Your token is now valid, you can use it in your application") ->
- ck
- String.contains?(resp.body, "token is now valid") ->
- ck
- resp.status_code == 302 && (resp.headers |> Enum.into(%{}) |> Map.has_key?("Location")) ->
- ck # presume the validation was successful if redirected to redirect uri
- true ->
- raise error_msg1
+ String.contains?(resp.body, "Invalid validity") -> {:error, error_msg2}
+ String.contains?(resp.body, "The token is now valid, it can be used in the application") -> {:ok, ck}
+ String.contains?(resp.body, "Your token is now valid, you can use it in your application") -> {:ok, ck}
+ String.contains?(resp.body, "token is now valid") -> {:ok, ck}
+ String.contains?(resp.body, "You have activated the double factor authentication") -> {:ok, :handle_2fa}
+ # presume the validation was successful if redirected to redirect url
+ resp.status_code == 302 && (resp.headers |> Enum.into(%{}) |> Map.has_key?("Location")) -> {:ok, ck}
+ true -> {:error, "Unexpected error " <> error_msg1}
end
+ end
+
+
+ defp build_2fa_request(resp_body) do
+ Mix.Shell.IO.info("You have activated 2FA on your OVH account, you need to verify your account via 2FA")
+ Floki.find(resp_body, "form input")
+ |> Enum.reduce("", fn({type, input, _options}, acc) ->
+ {name_val, value} =
+ cond do
+ type == "input" && {"name", "sessionId"} in input ->
+ name_val = :proplists.get_value("name", input)
+ value = :proplists.get_value("value", input)
+ {name_val, value}
+ type == "input" && {"name", "credentialToken"} in input ->
+ name_val = :proplists.get_value("name", input)
+ value = :proplists.get_value("value", input)
+ {name_val, value}
+ type == "input" && {"name", "duration"} in input ->
+ name_val = :proplists.get_value("name", input)
+ value = "0"
+ {name_val, value}
+ type == "input" && {"type", "number"} in input && {"placeholder", "Code"} in input ->
+ name_val = :proplists.get_value("name", input)
+ # Get value from shell asking user for 2FA code.
+ value = Mix.Shell.IO.prompt("Please enter *promptly* the 2FA (2 Factor Authentication) code generated by your mobile application:")
+ |> String.trim()
+ Mix.Shell.IO.info("The code #{value} will be sent as the 2FA code")
+ {name_val, value}
+ true ->
+ # raise "Unexpected input"
+ {:no_name, :no_val}
+ end
+ case {name_val, value} do
+ {:no_name, :no_val} -> acc
+ {name_val, value} -> acc <> name_val <> "=" <> value <> "&"
+ end
+ end)
+ |> Kernel.<>("otpMethod" <> "=" <> "totp")
end
- defp get_credentials(opts_map) do
- Og.context(__ENV__, :debug)
+ defp handle_2fa(resp_body, validation_url, ck) do
+ method = :post
+ url = validation_url
+ body = build_2fa_request(resp_body)
+ headers = [{"Content-Type", "application/x-www-form-urlencoded"}]
+ options = @default_hackney_options
+
+ conn = %HTTPipe.Conn{
+ request: %HTTPipe.Request{
+ method: method,
+ url: url,
+ body: body,
+ headers: headers
+ },
+ adapter: @default_adapter,
+ adapter_options: options
+ }
+ {:ok, conn} = HTTPipe.Conn.execute(conn)
+
+ error_msg = "function check_for_successful_binding seems to be entering an error loop"
+ case check_for_successful_binding(conn.response, validation_url, ck) do
+ {:ok, :handle_2fa} -> raise error_msg
+ {:ok, ck} -> ck
+ {:error, msg} -> raise "#{error_msg} - #{msg}"
+ end
+ end
+
+ defp get_credentials(opts_map) do
create_app_body = get_app_create_page(opts_map) |> get_create_app_inputs() |> build_app_request(opts_map) |> send_app_request(opts_map)
opts_map = Map.merge(opts_map, %{
application_key: get_application_key(create_app_body),
application_secret: get_application_secret(create_app_body),
application_name: get_application_name(create_app_body),
application_description: get_application_description(create_app_body)
- })
+ })
ck = get_consumer_key(opts_map) |> bind_consumer_key_to_app(opts_map)
- Map.merge(opts_map, %{ consumer_key: ck })
+ Map.merge(opts_map, %{consumer_key: ck})
|> Map.delete(:login) |> Map.delete(:password)
end
@@ -430,45 +516,45 @@ defmodule Mix.Tasks.Ovh do
end
+ defp config_names(app_name, client_name) when is_atom(app_name) do
+ config_names(Atom.to_string(app_name), client_name)
+ end
defp config_names(app_name, client_name) do
- Og.context(__ENV__, :debug)
-
{config_header, mod_client_name} =
case app_name do
"ex_ovh" ->
{
":" <> app_name,
"EX_OVH_"
- }
+ }
other ->
- client_name =
- case client_name do
- :nil -> "OvhClient"
- client_name -> client_name
- end
+ client_name = client_name && client_name || "OvhClient"
{
":" <> app_name <> ", " <> Macro.camelize(app_name) <> "." <> client_name,
- String.upcase(other) <> "_" <> Morph.to_snake_caps(client_name) <>"_"
- }
+ String.upcase(other) <> "_" <> String.upcase(Macro.underscore(client_name)) <>"_"
+ }
end
{config_header, mod_client_name}
end
- defp create_or_update_env_file(options) do
+ defp create_or_update_env_file(options, elixir_app_name) do
env_path = ".env"
- File.touch!(env_path)
+ File.exists?(env_path) || File.touch!(env_path)
existing = File.read!(env_path)
- {_config_header, mod_client_name} = config_names(options.application_name, options.client_name)
- format_date = ExOvh.Utils.formatted_date()
+ app_name = elixir_app_name || options.application_name
+ {_config_header, mod_client_name} = config_names(app_name, options.client_name)
+ existing =
+ case existing do
+ "" -> "#!/usr/bin/env bash\n"
+ _ -> existing
+ end
new = existing <>
~s"""
- # updated on #{format_date}
+ # updated on #{formatted_date()}
export #{mod_client_name <> "APPLICATION_KEY"}=\"#{options.application_key}\"
export #{mod_client_name <> "APPLICATION_SECRET"}="#{options.application_secret}\"
export #{mod_client_name <> "CONSUMER_KEY"}="#{options.consumer_key}\"
- export #{mod_client_name <> "ENDPOINT"}=\"#{options.endpoint}\"
- export #{mod_client_name <> "API_VERSION"}=\"#{options.api_version}\"
"""
{:ok, file} = File.open(env_path, [:write, :utf8])
@@ -478,9 +564,9 @@ defmodule Mix.Tasks.Ovh do
end
- defp print_config(options) do
- Og.context(__ENV__, :debug)
- {config_header, mod_client_name} = config_names(options.application_name, options.client_name)
+ defp print_config(options, elixir_app_name) do
+ app_name = elixir_app_name || options.application_name
+ {config_header, mod_client_name} = config_names(app_name, options.client_name)
~s"""
@@ -491,11 +577,19 @@ defmodule Mix.Tasks.Ovh do
application_key: System.get_env(\"#{mod_client_name <> "APPLICATION_KEY"}\"),
application_secret: System.get_env(\"#{mod_client_name <> "APPLICATION_SECRET"}\"),
consumer_key: System.get_env(\"#{mod_client_name <> "CONSUMER_KEY"}\"),
- endpoint: System.get_env(\"#{mod_client_name <> "ENDPOINT"}\"),
- api_version: System.get_env(\"#{mod_client_name <> "API_VERSION"}\") || "1.0"
- ]
+ endpoint: \"#{options.endpoint}\",
+ api_version: \"#{options.api_version}\"
+ ]
"""
end
+ defp formatted_date() do
+ {year, month, date} = :erlang.date()
+ Integer.to_string(date) <> "." <>
+ Integer.to_string(month) <> "." <>
+ Integer.to_string(year)
+ end
+
+
end
\ No newline at end of file
diff --git a/lib/ovh/v1/cloud/cloudstorage/query.ex b/lib/ovh/v1/cloud/cloudstorage/query.ex
deleted file mode 100644
index ff07586..0000000
--- a/lib/ovh/v1/cloud/cloudstorage/query.ex
+++ /dev/null
@@ -1,265 +0,0 @@
-defmodule ExOvh.Ovh.V1.Cloud.Cloudstorage.Query do
- @moduledoc ~s"""
- Helper functions for building queries directed at the cloudstorage related parts of the `/cloud` requests.
-
- See `ExOvh.Ovh.V1.Cloud.Query` for generic cloud requests.
-
- ## Functions Summary
-
- | Function | Description | OVH API call |
- |---|---|---|
- | `get_containers/1` | <small>Get containers for a given swift tenant id (project id or ovh service name)</small> | <sub><sup>GET /cloud/project/{serviceName}/storage </sup></sub> |
- | `create_container/3` | <small>Create a container for a given tenant_id (ovh service_name), a container and a region.</small> | <sub><sup>POST /cloud/project/{serviceName}/storage</sup></sub> |
- | `get_access/1` | <small>Get access details for the Swift API for a given swift tenant_id (ovh service_name)</small> | <sub><sup>GET /cloud/project/{serviceName}/storage/access</sup></sub> |
- | `container_info/2` | <small>Gets details about a container such as objects, size, region, public or not, static_url, name, ...</small> | <sub><sup>GET /cloud/project/{serviceName}/storage/{containerId}</sup></sub> |
- | `delete_container/2` | <small>Deletes a given container.</small> | <sub><sup>DELETE /cloud/project/{serviceName}/storage/{containerId}</sup></sub> |
- | `modify_container_cors/3` | <small>Modify the CORS settings for a container. See [swift docs](http://docs.openstack.org/developer/swift/cors.html)</small> | <sub><sup>POST /cloud/project/{serviceName}/storage/{containerId}/cors Add CORS support on your container</sup></sub> |
- | `deploy_container_as_static_website/2` | <small>Deploy the container files as a static web site.</small> | <sub><sup>POST /cloud/project/{serviceName}/storage/{containerId}/static</sup></sub> |
-
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.get_containers(service_name) |> ExOvh.Ovh.request!()
- """
- alias ExOvh.Ovh.Query
-
-
- @doc ~s"""
- Get storage containers
-
- ## Api call
-
- GET /cloud/project/{serviceName}/storage
-
- ## Arguments
-
- - `service_name`: service name for the ovh cloud service
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.get_containers(service_name) |> ExOvh.Ovh.request!()
- """
- @spec get_containers(String.t) :: Query.t
- def get_containers(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/storage",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Create container
-
- ## Api call
-
- POST /cloud/project/{serviceName}/storage
-
- ## Arguments
-
- - `service_name`: service name for the ovh cloud service
- - `container_name`: name for the new container
- - `region`: region for the new container, defaults to "SBG1". See regions by running:
- Currently can choose from "GRA1", "BHS1", "SBG1".
-
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.create_container(service_name, "test_container") |> ExOvh.Ovh.request!()
- """
- @spec create_container(String.t, String.t, String.t) :: Query.t
- def create_container(service_name, container_name, region \\ "SBG1") do
- # POST /cloud/project/{serviceName}/storage Create container
- %Query{
- method: :post,
- uri: "/cloud/project/#{service_name}/storage",
- params: %{
- "containerName" => container_name,
- "region" => region
- } |> Poison.encode!()
- }
- end
-
-
- @doc ~s"""
- Gets the x_auth_token and the swift endpoints for a given tenant_id (ovh service_name). A different endpoint is returned
- depending on the region. Examples of regions include "BHS1", "SBG1", "GRA1". With these details, requests can be made through
- the Swift api.
-
- ## Api call
-
- GET /cloud/project/{serviceName}/storage/access
-
- ## Arguments
-
- - `service_name`: service name for the ovh cloud service
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.get_access(service_name) |> ExOvh.Ovh.request!()
- """
- @spec get_access(String.t) :: Query.t
- def get_access(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/storage/access",
- params: :nil
- }
- end
-
-
-
- @doc ~s"""
- Gets the details for a given container.
-
- Returns information such as a list of objects in the container, size of the container, whether the container is public
- or not, the region of the container, the name of the container, the number of stored objects for the container and the
- static url for the container.
-
- ## Api call
-
- GET /cloud/project/{serviceName}/storage/{containerId}
-
- ## Arguments
-
- - `service_name`: service name for the ovh cloud service
- - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.container_info(service_name, container_id) |> ExOvh.Ovh.request!()
- """
- @spec container_info(String.t, String.t) :: Query.t
- def container_info(service_name, container_id) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/storage/#{container_id}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Deletes a given container.
-
- *Note:* container_d is not the same as container_name.
-
- ## Api call
-
- DELETE /cloud/project/{serviceName}/storage/{containerId}
-
- ## Arguments
-
- - `service_name`: service name for the ovh cloud service
- - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.delete_container(service_name, container_id) |> ExOvh.Ovh.request!()
- """
- @spec delete_container(String.t, String.t) :: Query.t
- def delete_container(service_name, container_id) do
- %Query{
- method: :delete,
- uri: "/cloud/project/#{service_name}/storage/#{container_id}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Modify CORS settings for a container.
-
- Modifies the container metadata such that cross origin requests can be permitted.
- See [CORS section of swift docs](http://docs.openstack.org/developer/swift/cors.html) for more info. Ans see here for more
- on [CORS in general](http://enable-cors.org/resources.html)
-
- | Metadata | Use |
- | --- | --- |
- | X-Container-Meta-Access-Control-Allow-Origin | Origins to be allowed to make Cross Origin Requests, space separated. |
-
-
- *Note:* container_d is not the same as container_name.
-
- ## Api call
-
- DELETE /cloud/project/{serviceName}/storage/{containerId}
-
- ## Arguments
-
- - `service_name`: service name for the ovh cloud service
- - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
- - `origin`: an origin that may make cross origin requests to the container. Defaults to `{}` (none) if left absent.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.modify_container_cors(service_name, container_id, "http://localhost:4001/") |> ExOvh.Ovh.prepare_request() |> Og.log_return() |> ExOvh.Ovh.request!()
-
- ## Notes
-
- To get a full overview of the container details with all metadata, the Swift client should be used. To see the changes, try running the following
- command for the `container_name` associated with this `container_id`. In fact, the OVH functions are not really required, most changes can be made directly
- using queries sent via the `Swift.Cloudstorage` client.
-
- account = ExOvh.Swift.Cloudstorage.account()
- container = "test_container"
- Openstex.Swift.V1.Query.container_info(container, account) |> ExOvh.Swift.Cloudstorage.request!() |> Map.get(:headers) |> Map.get("X-Container-Meta-Access-Control-Allow-Origin")
- """
- @spec modify_container_cors(String.t, String.t, String.t) :: Query.t
- def modify_container_cors(service_name, container_id, origin \\ {}) do
- %Query{
- method: :post,
- uri: "/cloud/project/#{service_name}/storage/#{container_id}/cors",
- params: %{
- "origin" => origin
- } |> Poison.encode!()
- }
- end
-
-
-
- @doc ~s"""
- Deploy a container as a static website.
-
- Modifies the ACL settings for a container on the "X-Container-Read" header and also other container metadata.
- See [swift auth docs](http://docs.openstack.org/developer/swift/overview_auth.html),
- [swift acl middleware](http://docs.openstack.org/developer/swift/misc.html#module-swift.common.middleware.acl)
- and [swift account middleware](http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.tempauth)
- for more information.
-
- ## Api call
-
- POST /cloud/project/{serviceName}/storage/{containerId}/static
-
- ## Arguments
-
- - `service_name`: service name for the ovh cloud service
- - `container_id`: container_id for a given container. *Note*: this is not the same as the container_name.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.modify_container_cors(service_name, container_id, "http://localhost:4001/") |> ExOvh.Ovh.prepare_request() |> Og.log_return() |> ExOvh.Ovh.request!()
-
- ## Notes
-
- To get a full overview of the container details with all metadata, the Swift client should be used. To see the changes, try running the following
- command for the `container_name` associated with this `container_id`. In fact, the OVH functions are not really required, most changes can be made directly
- using queries sent via the `Swift.Cloudstorage` client.
-
- account = ExOvh.Swift.Cloudstorage.account()
- container = "test_container"
- Openstex.Swift.V1.Query.container_info(container, account) |> ExOvh.Swift.Cloudstorage.request!() |> Map.get(:headers)
- """
- @spec deploy_container_as_static_website(String.t, String.t) :: Query.t
- def deploy_container_as_static_website(service_name, container_id) do
- %Query{
- method: :post,
- uri: "/cloud/project/#{service_name}/storage/#{container_id}/static",
- params: :nil
- }
- end
-
-
-
-end
diff --git a/lib/ovh/v1/cloud/query.ex b/lib/ovh/v1/cloud/query.ex
deleted file mode 100644
index c342932..0000000
--- a/lib/ovh/v1/cloud/query.ex
+++ /dev/null
@@ -1,712 +0,0 @@
-defmodule ExOvh.Ovh.V1.Cloud.Query do
- @moduledoc ~s"""
- Helper functions for building queries directed at the `/cloud` part of the ovh api.
-
- ## Functions Summary
-
- | Function | Description | OVH API call |
- |---|---|---|
- | `list_services/0` | <small>List available services or list available cloud projects. A returned project id in OVH terms is similar to a tenant id in swift terms</small> | <sub><sup>GET /cloud/project</sup></sub> |
- | `get_users/1` | <small>Get all users</small> | <sub><sup>GET /cloud/project/{serviceName}/user</sup></sub> |
- | `create_user/2` | <small>Create user</small> | <sub><sup>POST /ctsloud/project/{serviceName}/user</sup></sub> |
- | `get_user_details/2` | <small>Get user details. Returns the user_id and username and other details.</small> | <sub><sup>GET /cloud/project/{serviceName}/user/{userId}</sup></sub> |
- | `delete_user/2` | <small>Delete user</small> | <sub><sup>DELETE /cloud/project/{serviceName}/user/{userId}</sup></sub> |
- | `download_openrc_script/3` | <small>Get RC file of OpenStack</small> | <sub><sup>GET /cloud/project/{serviceName}/user/{userId}/openrc</sup></sub> |
- | `regenerate_credentials/2` | <small>Regenerate user credentials including password</small> | <sub><sup>POST /cloud/project/{serviceName}/user/{userId}/regeneratePassword</sup></sub> |
- | `swift_identity/3` | <small>Gets a json object similar to that returned by Keystone Identity. Includes the 'X-Auth-Token'</small> | <sub><sup>POST /cloud/project/{serviceName}/user/{userId}/token</sup></sub> |
- | `create_project/2` | <small>Start a new cloud project in the OVH cloud. Corresponds to creating a new Swift stack with a new tenant_id.</small> | <sub><sup>POST /cloud/createProject</sup></sub> |
- | `get_prices/2` | <small>Get Prices for OVH cloud services.</small> | <sub><sup>GET /cloud/price</sup></sub> |
- | `project_info/1` | <small>Get information about a project with the project_id (tenant_id)</small> | <sub><sup>GET /cloud/project/{serviceName}</sup></sub> |
- | `modify_project/2` | <small>Modify a project properties. Change the project description.</small> | <sub><sup>PUT /cloud/project/{serviceName}</sup></sub> |
- | `project_administrative_info/1` | <small>Get administration information about the project.</small> | <sub><sup>GET /cloud/project/{serviceName}/serviceInfos</sup></sub> |
- | `project_quotas/1` | <small>Get project quotas.</small> | <sub><sup>GET /cloud/project/{serviceName}/quota</sup></sub> |
- | `project_regions/1` | <small>Get project regions.</small> | <sub><sup>GET /cloud/project/{serviceName}/region</sup></sub> |
- | `project_region_info/2` | <small>Get details about a project region.</small> | <sub><sup>GET /cloud/project/{serviceName}/region/{regionName}</sup></sub> |
- | `project_consumption/3` | <small>Get details about a project consumption for a given `date_from` and `date_to`.</small> | <sub><sup>GET /cloud/project/{serviceName}/consumption</sup></sub> |
- | `project_bills/3` | <small>Get details about a project billing for a given `date_from` and `date_to`..</small> | <sub><sup>GET /cloud/project/{serviceName}/bill</sup></sub> |
- | `create_project_alert/4` | <small>Add a new project alert</small> | <sub><sup>POST /cloud/project/{serviceName}/alerting</sup></sub> |
- | `get_project_alert_info/2` | <small>Get detailed information about a project alert.</small> | <sub><sup>GET /cloud/project/{serviceName}/alerting/{id}</sup></sub> |
- | `modify_project_alert/5` | <small>Modify an existing project alert.</small> | <sub><sup>PUT /cloud/project/{serviceName}/alerting/{id}</sup></sub> |
- | `delete_project_alert/2` | <small>Delete an existing project alert.</small> | <sub><sup>DELETE /cloud/project/{serviceName}/alerting/{id}</sup></sub> |
- | `terminate_service/2` | <small>Terminate a cloud project.</small> | <sub><sup>POST /cloud/project/{serviceName}/terminate</sup></sub> |
-
-
- ## TO BE ADDED
-
- GET /cloud/project/{serviceName}/acl
- POST /cloud/project/{serviceName}/acl
- GET /cloud/project/{serviceName}/acl/{accountId}
- DELETE /cloud/project/{serviceName}/acl/{accountId}
-
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Cloudstorage.Query.get_containers(service_name) |> ExOvh.Ovh.request!()
- """
- alias ExOvh.Ovh.Query
-
-
- @doc ~s"""
- List available services
-
- ## Api Call
-
- GET /cloud/project
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.list_services() |> ExOvh.Ovh.request!()
- """
- @spec list_services() :: Query.t
- def list_services() do
- %Query{
- method: :get,
- uri: "/cloud/services",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Get all users
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/user
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.get_users(service_name) |> ExOvh.Ovh.request!()
- """
- @spec get_users(String.t) :: Query.t
- def get_users(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/user",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Create user
-
- ## Api Call
-
- POST /cloud/project/{serviceName}/user
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `description`: description ascribed to the new user.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.create_user(service_name, "ex_ovh") |> ExOvh.Ovh.request!()
- """
- @spec create_user(String.t, String.t) :: Query.t
- def create_user(service_name, description) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/user",
- params: %{
- "description" => description
- }
- |> Poison.encode!()
- }
- end
-
-
- @doc ~s"""
- Get user details. Returns the user_id and username and other details.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/user/{userId}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `user_id`: corresponds to user_id. See `get_users/1`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.get_user_details(service_name, user_id) |> ExOvh.Ovh.request!()
- """
- @spec get_user_details(String.t, String.t) :: Query.t
- def get_user_details(service_name, user_id) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/user/#{user_id}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Delete a specific user.
-
- ## Api Call
-
- DELETE /cloud/project/{serviceName}/user/{userId}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `user_id`: The user_id. See `get_users/1`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.delete_user(service_name, user_id) |> ExOvh.Ovh.request!()
- """
- @spec delete_user(String.t, String.t) :: Query.t
- def delete_user(service_name, user_id) do
- %Query{
- method: :delete,
- uri: "/cloud/project/#{service_name}/user/#{user_id}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Get RC file of OpenStack. This file is a bash script with much of the openstack credentials. Makes it easier for
- setting up a swift client from the terminal.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/user/{userId}/openrc
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `user_id`: user_id for user accessing the service.
- - `region`: region for which the rc file will be created. Defaults to "SBG1" if left absent.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.download_openrc_script(service_name, user_id, "SBG1") |> ExOvh.Ovh.request!()
- """
- @spec download_openrc_script(String.t, String.t, String.t) :: Query.t
- def download_openrc_script(service_name, user_id, region \\ "SBG1") do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/user/#{user_id}/openrc",
- params: %{
- region: region
- }
- }
- end
-
-
- @doc ~s"""
- Regenerate user password and other credentials.
-
- ## Api Call
-
- POST /cloud/project/{serviceName}/user/{userId}/regeneratePassword
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `user_id`: user_id for accessing the project. See `get_users/1`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.regenerate_credentials(service_name, user_id) |> ExOvh.Ovh.request!()
- """
- @spec regenerate_credentials(String.t, String.t) :: Query.t
- def regenerate_credentials(service_name, user_id) do
- %Query{
- method: :post,
- uri: "/cloud/project/#{service_name}/user/#{user_id}/regeneratePassword",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Get the token for the user (very similar to keystone identity)
-
- ## Api Call
-
- POST /cloud/project/{serviceName}/user/{userId}/token
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `user_id`: The swift user_id to login with. See `get_users/1`.
- - `password`: The swift password to login with. See `regenerate_credentials/2`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.swift_identity(service_name, user_id) |> ExOvh.Ovh.request!()
- """
- @spec swift_identity(String.t, String.t, String.t) :: Query.t
- def swift_identity(service_name, user_id, password) do
- %Query{
- method: :post,
- uri: "/cloud/project/#{service_name}/user/#{user_id}/token",
- params: %{
- "password" => password
- }
- |> Poison.encode!()
- }
- end
-
-
- @doc ~s"""
- Create a new Cloud Project.
-
- ## Api Call
-
- POST /cloud/createProject
-
- ## Arguments
-
- - `description`: project description
- - `voucher`: ovh voucher code
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.create_project(description, voucher) |> ExOvh.Ovh.request!()
- """
- @spec create_project(String.t, String.t) :: Query.t
- def create_project(description, voucher) do
- %Query{
- method: :post,
- uri: "/cloud/createProject",
- params: %{
- "description" => description,
- "voucher" => voucher
- }
- |> Poison.encode!()
- }
- end
-
-
- @doc ~s"""
- Get services prices for the OVH public cloud.
-
- ## Api Call
-
- GET /cloud/price
-
- ## Arguments
-
- - `region`: prices for a particular region (optional)
- - `flavor_id`: ovh voucher code (optional)
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.get_prices() |> ExOvh.Ovh.request!()
- """
- @spec get_prices(String.t | :nil, String.t | :nil) :: Query.t
- def get_prices(region \\ :nil, flavor_id \\ :nil) do
- params =
- cond do
- region == :nil and flavor_id == :nil -> :nil
- region != :nil and flavor_id == :nil -> %{"region" => region}
- region == :nil and flavor_id != :nil -> %{"flavorId" => flavor_id}
- region != :nil and flavor_id != :nil -> %{ "region" => region, "flavorId" => flavor_id }
- end
- %Query{
- method: :get,
- uri: "/cloud/createProject",
- params: params
- }
- end
-
-
- @doc ~s"""
- Get details for a given project.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.project_info(service_name) |> ExOvh.Ovh.request!()
- """
- @spec project_info(String.t) :: Query.t
- def project_info(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Modify the project description for a project.
-
- ## Api Call
-
- PUT /cloud/project/{serviceName}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.modify_project(service_name, new_description) |> ExOvh.Ovh.request!()
- """
- @spec modify_project(String.t, String.t) :: Query.t
- def modify_project(service_name, new_description) do
- %Query{
- method: :put,
- uri: "/cloud/project/#{service_name}",
- params: %{
- "description" => new_description
- }
- |> Poison.encode!()
- }
- end
-
-
- @doc ~s"""
- Get administration information about the project
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/serviceInfos
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.project_administrative_info(service_name) |> ExOvh.Ovh.request!()
- """
- @spec project_administrative_info(String.t) :: Query.t
- def project_administrative_info(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/serviceInfos",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Get project quotas.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/quota
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.project_quotas(service_name) |> ExOvh.Ovh.request!()
- """
- @spec project_quotas(String.t) :: Query.t
- def project_quotas(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/quota",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Get project regions.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/region
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.project_regions(service_name) |> ExOvh.Ovh.request!()
- """
- @spec project_regions(String.t) :: Query.t
- def project_regions(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/region",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Get project details about a project region.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/region/{regionName}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.project_region_info(service_name) |> ExOvh.Ovh.request!()
- """
- @spec project_region_info(String.t, String.t) :: Query.t
- def project_region_info(service_name, region_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/region/#{region_name}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Get project details about a project consumption.
-
- *Note:* Will only allow for up to one month of data to be returned.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/consumption
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `date_from`: starting date in `ISO 8601` format. defaults to 4 weeks/28 days ago (UTC time) if left absent.
- - `date_to`: end date in `ISO 8601` format. defaults to now (UTC time) if left absent.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.project_consumption(service_name) |> ExOvh.Ovh.request!()
- """
- @spec project_consumption(String.t, String.t, String.t) :: Query.t
- def project_consumption(service_name, date_from \\ :nil, date_to \\ :nil) do
- date_from = if date_from == :nil, do: Calendar.DateTime.now_utc!() |> Calendar.DateTime.add!(-(60*60*24*28)) |> Calendar.DateTime.Format.rfc3339(), else: date_from
- date_to = if date_to == :nil, do: Calendar.DateTime.now_utc!() |> Calendar.DateTime.Format.rfc3339(), else: date_to
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/consumption",
- params: %{from: date_from, to: date_to}
- }
- end
-
-
- @doc ~s"""
- Get project details about a project bills.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/bill
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `date_from`: starting date in `ISO 8601` format. defaults to 4 weeks/28 days ago (UTC time) if left absent.
- - `date_to`: end date in `ISO 8601` format. defaults to now (UTC time) if left absent.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.project_bills(service_name) |> ExOvh.Ovh.request!()
- """
- @spec project_bills(String.t, String.t, String.t) :: Query.t
- def project_bills(service_name, date_from \\ :nil, date_to \\ :nil) do
- date_from = if date_from == :nil, do: Calendar.DateTime.now_utc!() |> Calendar.DateTime.add!(-(60*60*24*28)) |> Calendar.DateTime.Format.rfc3339(), else: date_from
- date_to = if date_to == :nil, do: Calendar.DateTime.now_utc!() |> Calendar.DateTime.Format.rfc3339(), else: date_to
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/bill",
- params: %{from: date_from, to: date_to}
- }
- end
-
-
- @doc ~s"""
- Get a list of project alert ids. These project alert ids can then be looked up in a separate query for more information.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/alerting
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.get_project_alerts(service_name) |> ExOvh.Ovh.request!()
- """
- @spec get_project_alerts(String.t) :: Query.t
- def get_project_alerts(service_name) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/alerting",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Create a new project alert.
-
- *Notes:*
- It seems only one alert is allowed per project. To create a new one alter the old one or delete the old one and add a new one.
- Once the monthly threshold in the given currency is exceeded, then the alert email is sent.
-
- ## Api Call
-
- POST /cloud/project/{serviceName}/alerting
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `delay`: The delay between each alert in seconds. This has to be selected from an enumerable (a list). 3600 is the lowest. defaults to 3600. (1 hour)
- - `email`: The email to send the alert to.
- - `monthlyThreshold`: The maximum monetary (cash) usage allowed in one month. This is an integer value. Ask OVH about how the currency is chosen.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.create_project_alert(service_name, "email_address@email.email", 5) |> ExOvh.Ovh.request!()
- """
- @spec create_project_alert(String.t, String.t, integer, String.t) :: Query.t | no_return
- def create_project_alert(service_name, email, monthly_threshold, delay \\ "3600") do
- unless is_integer(monthly_threshold), do: Og.log_return(__ENV__, "monthly_threshold should be an integer!", :error) |> raise()
- %Query{
- method: :post,
- uri: "/cloud/project/#{service_name}/alerting",
- params: %{
- "delay" => delay,
- "email" => email,
- "monthlyThreshold" => monthly_threshold
- } |> Poison.encode!()
- }
- end
-
-
- @doc ~s"""
- Get detailed information about a project alert.
-
- ## Api Call
-
- GET /cloud/project/{serviceName}/alerting/{id}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `alert_id`: The id of the project alert. See `get_project_alerts/1`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.get_project_alert_info(service_name, alert_id) |> ExOvh.Ovh.request!()
- """
- @spec get_project_alert_info(String.t, String.t) :: Query.t
- def get_project_alert_info(service_name, alert_id) do
- %Query{
- method: :get,
- uri: "/cloud/project/#{service_name}/alerting/#{alert_id}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Modify an existing project alert.
-
- ## Api Call
-
- PUT /cloud/project/{serviceName}/alerting/{id}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `alert_id`: The alert to be modified.
- - `delay`: The delay between each alert in seconds. This has to be selected from an enumerable (a list). 3600 is the lowest. defaults to 3600. (1 hour)
- - `email`: The email to send the alert to.
- - `monthlyThreshold`: The maximum monetary (cash) usage allowed in one month. This is an integer value. Ask OVH about how the currency is chosen.
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.modify_project_alert(service_name, alert_id, "email_address@email.email", 5) |> ExOvh.Ovh.request!()
- """
- @spec modify_project_alert(String.t, String.t, String.t, integer, String.t) :: Query.t
- def modify_project_alert(service_name, alert_id, email, monthly_threshold, delay \\ "3600") do
- unless is_integer(monthly_threshold), do: Og.log_return(__ENV__, "monthly_threshold should be an integer!", :error) |> raise()
- %Query{
- method: :put,
- uri: "/cloud/project/#{service_name}/alerting/#{alert_id}",
- params: %{
- "delay" => delay,
- "email" => email,
- "monthlyThreshold" => monthly_threshold
- } |> Poison.encode!()
- }
- end
-
-
- @doc ~s"""
- Delete a project alert.
-
- ## Api Call
-
- DELETE /cloud/project/{serviceName}/alerting/{id}
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
- - `alert_id`: The id of the project alert. See `get_project_alerts/1`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.get_project_alert_info(service_name, alert_id) |> ExOvh.Ovh.request!()
- """
- @spec delete_project_alert(String.t, String.t) :: Query.t
- def delete_project_alert(service_name, alert_id) do
- %Query{
- method: :delete,
- uri: "/cloud/project/#{service_name}/alerting/#{alert_id}",
- params: :nil
- }
- end
-
-
- @doc ~s"""
- Terminate a cloud project.
-
- ## Api Call
-
- POST /cloud/project/{serviceName}/terminate
-
- ## Arguments
-
- - `service_name`: corresponds to project_id or tenant_id. See `list_services/0`
-
- ## Example
-
- ExOvh.Ovh.V1.Cloud.Query.terminate_project(service_name) |> ExOvh.Ovh.request!()
- """
- @spec terminate_project(String.t) :: Query.t
- def terminate_project(service_name) do
- %Query{
- method: :post,
- uri: "/cloud/project/#{service_name}/terminate",
- params: :nil
- }
- end
-
-
-
-end
\ No newline at end of file
diff --git a/lib/ovh/v1/webstorage/query.ex b/lib/ovh/v1/webstorage/query.ex
deleted file mode 100644
index cc3ac3f..0000000
--- a/lib/ovh/v1/webstorage/query.ex
+++ /dev/null
@@ -1,170 +0,0 @@
-defmodule ExOvh.Ovh.V1.Webstorage.Query do
- @moduledoc ~s"""
- Helper functions for building `queries directed at the `/cdn/webstorage` part of the custom ovh api.
-
- ## Functions Summary
-
- | Function | Description | OVH API call |
- |---|---|---|
- | `get_services/0` | <small>Get a list of all webstorage cdn services.</small> | <sub><sup>GET /v1/cdn/webstorage</sup></sub> |
- | `get_service/1` | <small>Get the domain, server and storage limits for a specific webstorage cdn service</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}</sup></sub> |
- | `get_service_info/1` | <small>Get a administrative details for a specific webstorage cdn service</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}/serviceInfos</sup></sub> |
- | `get_service_stats/2` | <small>Get statistics for a specific webstorage cdn service</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}/statistics</sup></sub> |
- | `get_credentials/1` | <small>Get credentials for using the swift compliant api</small> | <sub><sup>GET /v1/cdn/webstorage/{serviceName}/statistics</sup></sub> |
-
-
- ## Example
-
- ExOvh.Ovh.V1.Webstorage.Query.get_all_webstorage() |> ExOvh.Ovh.request()
- """
- alias ExOvh.Ovh.Query
-
-
-
- @doc ~s"""
- Get a list of all webstorage cdn services available for the client account
-
- ## Api call
-
- GET /v1/cdn/webstorage
-
- ## Example
-
- ExOvh.Ovh.V1.Webstorage.Query. get_services() |> ExOvh.Ovh.request()
- """
- @spec get_services() :: Query.t
- def get_services() do
- %Query{
- method: :get,
- uri: "/cdn/webstorage",
- params: :nil
- }
- end
-
-
-
- @doc ~s"""
- Get the domain, server and storage limits for a specific webstorage cdn service
-
- ## Api call
-
- GET /v1/cdn/webstorage/{serviceName}
-
- ## Arguments
-
- - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
-
- ## Example
-
- alias ExOvh.Ovh.V1.Webstorage.Query
- service_name = "cdnwebstorage-????"
- query = Query.get_service(service_name)
- {:ok, resp} = ExOvh.Ovh.request(query)
- %{
- "domain" => domain,
- "storageLimit => storage_limit,
- "server" => server
- } = resp.body
- """
- @spec get_service(String.t) :: Query.t
- def get_service(service_name) do
- %Query{
- method: :get,
- uri: "/cdn/webstorage/",
- params: service_name
- }
- end
-
-
-
- @doc ~s"""
- Get a administrative details for a specific webstorage cdn service
-
- ## Api call
-
- GET /v1/cdn/webstorage/{serviceName}/serviceInfos
-
- ## Arguments
-
- - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
-
- ## Example
-
- alias ExOvh.Ovh.V1.Webstorage.Query
- service_name = "cdnwebstorage-????"
- Query.get_service_info(service_name)
- {:ok, resp} = ExOvh.Ovh.request(query)
- """
- @spec get_service_info(String.t) :: Query.t
- def get_service_info(service_name) do
- %Query{
- method: :get,
- uri: "/cdn/webstorage/#{service_name}/serviceInfos",
- params: :nil
- }
- end
-
-
-
- @doc ~s"""
- Get statistics for a specific webstorage cdn service
-
- ## Api call
-
- GET /v1/cdn/webstorage/{serviceName}/statistics
-
- ## Arguments
-
- - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
- - `options`:
- - `period can be "month", "week" or "day"`
- - `type can be "backend", "quota" or "cdn"`
-
- ## Example
-
- alias ExOvh.Ovh.V1.Webstorage.Query
- service_name = "cdnwebstorage-????"
- query = Query.get_service_stats(service_name, [period: "month", type: "backend"])
- {:ok, resp} = ExOvh.Ovh.request(query)
- """
- @spec get_service_stats(String.t, Keyword.t) :: Query.t
- def get_service_stats(service_name, opts \\ []) do
- period = Keyword.get(opts, "period", "month")
- type = Keyword.get(opts, "type", "cdn")
- %Query{
- method: :get,
- uri: "/cdn/webstorage/#{service_name}/statistics",
- params: %{"period" => period, "type" => type}
- }
- end
-
-
-
- @doc ~s"""
- Get credentials for using the swift compliant api
-
- ## Api call
-
- GET /v1/cdn/webstorage/{serviceName}/credentials
-
- ## Arguments
-
- - `service_name`: Name of the Webstorage CDN service - assigned by OVH.
-
- ## Example
-
- alias ExOvh.Ovh.V1.Webstorage.Query
- service_name = "cdnwebstorage-????"
- query = Query.get_webstorage_credentials(service_name)
- {:ok, resp} = ExOvh.Ovh.request(query)
- """
- @spec get_credentials(String.t) :: ExOvh.Query.Ovh.t
- def get_credentials(service_name) do
- %Query{
- method: :get,
- uri: "/cdn/webstorage/#{service_name}/credentials",
- params: :nil
- }
- end
-
-end
diff --git a/lib/query.ex b/lib/query.ex
deleted file mode 100644
index 7d69cdc..0000000
--- a/lib/query.ex
+++ /dev/null
@@ -1,5 +0,0 @@
-defmodule ExOvh.Ovh.Query do
- @moduledoc false
- defstruct [:method, :uri, :params, service: :ovh]
- @type t :: %__MODULE__{method: atom, uri: String.t, params: any, service: :ovh}
-end
\ No newline at end of file
diff --git a/lib/request/ovh/request.ex b/lib/request/ovh/request.ex
deleted file mode 100644
index f7cee64..0000000
--- a/lib/request/ovh/request.ex
+++ /dev/null
@@ -1,49 +0,0 @@
-defimpl Openstex.Request, for: ExOvh.Ovh.Query do
- @moduledoc :false
-
- alias Openstex.{Auth, Response}
- alias ExOvh.Ovh.Query
-
-
- # Public
-
-
- @spec request(Query.t, Keyword.t, atom) :: {:ok, Response.t} | {:error, Response.t}
- def request(query, httpoison_opts, client) do
- Og.context(__ENV__, :debug)
-
- q = Auth.prepare_request(query, httpoison_opts, client) |> Map.from_struct()
-
- options = Keyword.merge(q.options, httpoison_opts)
- case HTTPoison.request(q.method, q.uri, q.body, q.headers, options) do
- {:ok, resp} ->
- body = parse_body(resp)
- resp = %Response{ body: body, headers: resp.headers |> Enum.into(%{}), status_code: resp.status_code }
- if resp.status_code >= 100 and resp.status_code < 400 do
- {:ok, resp}
- else
- {:error, resp}
- end
- {:error, resp} ->
- {:error, %HTTPoison.Error{reason: resp.reason}}
- end
-
- end
-
-
- # private
-
-
- defp parse_body(resp) do
- try do
- resp.body |> Poison.decode!()
- rescue
- _ ->
- resp.body
- end
- end
-
-
-end
-
-
diff --git a/lib/supervisor.ex b/lib/supervisor.ex
deleted file mode 100644
index 9a176e5..0000000
--- a/lib/supervisor.ex
+++ /dev/null
@@ -1,62 +0,0 @@
-defmodule ExOvh.Supervisor do
- @moduledoc :false
-
- use Supervisor
- alias ExOvh.Ovh.Defaults
- alias ExOvh.Auth.Ovh.Cache, as: OvhCache
- alias ExOvh.Auth.Openstack.Swift.Cache, as: SwiftCache
-
-
-
- # Public
-
-
- def start_link(client, _opts) do
- Og.context(__ENV__, :debug)
- Supervisor.start_link(__MODULE__, client, [name: client])
- end
-
-
- # Callbacks
-
-
- def init(client) do
- Og.context(__ENV__, :debug)
-
-
- webstorage_config = Keyword.get(client.config(), :swift, []) |> Keyword.get(:webstorage, :nil)
- cloudstorage_config = Keyword.get(client.config(), :swift, []) |> Keyword.get(:cloudstorage, :nil)
- |> Keyword.merge(Defaults.cloudstorage(), fn(_k, v1, v2) -> if v1 == :nil, do: v2, else: v1 end)
-
- ovh_client = Module.concat(client, Ovh)
- sup_tree = [
- {ovh_client, {OvhCache, :start_link, [ovh_client]}, :permanent, 10_000, :worker, [OvhCache]}
- ]
-
- sup_tree =
- case webstorage_config do
- :nil ->
- Og.log("No webstorage config found. Skipping initiation of OVH webstorage cdn service", :debug)
- sup_tree
- _webstorage_config ->
- webstorage_client = Module.concat(client, Swift.Webstorage)
- sup_tree ++
- [{webstorage_client, {SwiftCache, :start_link, [{ovh_client, webstorage_client}]}, :permanent, 10_000, :worker, [SwiftCache]}]
- end
-
- sup_tree =
- case cloudstorage_config do
- :nil ->
- Og.log("No cloudstorage config found. Skipping initiation of OVH cloudstorage service", :debug)
- sup_tree
- _cloudstorage_config ->
- cloudstorage_client = Module.concat(client, Swift.Cloudstorage)
- sup_tree ++
- [{cloudstorage_client, {SwiftCache, :start_link, [{ovh_client, cloudstorage_client}]}, :permanent, 10_000, :worker, [SwiftCache]}]
- end
-
- supervise(sup_tree, strategy: :one_for_one, max_restarts: 30)
- end
-
-
-end
diff --git a/lib/utils/utils.ex b/lib/utils/utils.ex
deleted file mode 100644
index ecfa238..0000000
--- a/lib/utils/utils.ex
+++ /dev/null
@@ -1,34 +0,0 @@
-defmodule ExOvh.Utils do
- @moduledoc false
-
- @doc """
- For naming an ets table to incorporate the name of the client.
-
- The client name is required so that when a client makes a request, the correct ets table
- is looked up if there are multiple clients in use.
- """
- defmacro ets_tablename(client) do
- # caller = __CALLER__.module
- quote do
- "Ets."
- <>
- (
- unquote(client) |> Atom.to_string()
- )
- |> String.to_atom()
- end
- end
-
- @doc """
- Returns a string with the formatted date
- """
- @spec formatted_date() :: String.t
- def formatted_date() do
- {year, month, date} = :erlang.date()
- Integer.to_string(date) <> "." <>
- Integer.to_string(month) <> "." <>
- Integer.to_string(year)
- end
-
-
-end
diff --git a/mix.exs b/mix.exs
index 4d96f7b..4bf3dc1 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,13 +1,15 @@
defmodule ExOvh.Mixfile do
use Mix.Project
+ @version "0.4.0"
+ @elixir "~> 1.5"
def project do
[
app: :ex_ovh,
name: "ExOvh",
- version: "0.0.1",
+ version: @version,
source_url: "https://github.com/stephenmoloney/ex_ovh",
- elixir: "~> 1.2",
+ elixir: @elixir,
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
description: description(),
@@ -19,28 +21,26 @@ defmodule ExOvh.Mixfile do
def application() do
[
- mod: [],
- applications: [:calendar, :crypto, :logger, :openstex]
+ applications: [:calendar, :crypto, :hackney, :floki, :logger, :poison]
]
end
defp deps() do
[
- {:secure_random, "~> 0.2"},
- {:floki, "~> 0.7.1"},
- {:calendar, "~> 0.13.2"},
- {:og, "~> 0.1"},
- {:morph, "~> 0.1.0"},
- {:openstex, github: "stephenmoloney/openstex", branch: "master"},
+ {:calendar, "~> 0.17"},
+ {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0"},
+ {:httpipe_adapters_hackney, "~> 0.11"},
+ {:floki, "~> 0.18"},
+ # dev deps
{:markdown, github: "devinus/markdown", only: :dev},
- {:ex_doc, "~> 0.11", only: :dev}
+ {:ex_doc, "~> 0.18", only: :dev}
]
end
defp description() do
~s"""
- An elixir client library for easier use of the Ovh api.
+ An elixir client library for the OVH API.
"""
end
@@ -50,18 +50,16 @@ defmodule ExOvh.Mixfile do
licenses: ["MIT"],
maintainers: ["Stephen Moloney"],
links: %{ "GitHub" => "https://github.com/stephenmoloney/ex_ovh"},
- files: ~w(lib priv mix.exs README* LICENSE* CHANGELOG*)
+ files: ~w(lib mix.exs README* LICENCE* CHANGELOG*)
}
end
defp docs() do
[
- main: "api-reference",
+ main: "ExOvh",
extras: [
- "docs/getting_started_basic.md": [path: "getting_started_basic.md", title: "Getting Started (Basic)"],
- "docs/getting_started_advanced.md": [path: "getting_started_advanced.md", title: "Getting Started (Advanced)"],
- "docs/mix_task_basic.md": [path: "mix_task_basic.md", title: "Basic Mix Task (Optional)"],
- "docs/mix_task_advanced.md": [path: "mix_task_advanced.md", title: "Advanced Mix Task (Optional)"]
+ "docs/mix_task.md": [path: "mix_task.md", title: "Step 1: Generating the OVH application"],
+ "docs/getting_started.md": [path: "getting_started.md", title: "Step 2: Setting up client"]
]
]
end
diff --git a/mix.lock b/mix.lock
new file mode 100644
index 0000000..5fdf36e
--- /dev/null
+++ b/mix.lock
@@ -0,0 +1,18 @@
+%{"calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
+ "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"},
+ "earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [], [], "hexpm"},
+ "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
+ "floki": {:hex, :floki, "0.18.1", "6f903e3074357fe9756079d0f607e430589912f698b5c5e5970af08daba1537c", [], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
+ "hackney": {:hex, :hackney, "1.10.1", "c38d0ca52ea80254936a32c45bb7eb414e7a96a521b4ce76d00a69753b157f21", [], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
+ "hoedown": {:git, "https://github.com/hoedown/hoedown.git", "980b9c549b4348d50b683ecee6abee470b98acda", []},
+ "httpipe": {:hex, :httpipe, "0.9.0", "4db66493b0ec2a86d142ea959a62e221d6ddb23ab48a676b691be3a16c38a415", [], [], "hexpm"},
+ "httpipe_adapters_hackney": {:hex, :httpipe_adapters_hackney, "0.11.0", "35c31b96fd6fea117f9ba6ca70467ada111dffb9f2fa7fca0bfc7e12bb166e8e", [], [{:hackney, "~> 1.8 or ~> 1.7 or ~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:httpipe, "~> 0.9.0", [hex: :httpipe, repo: "hexpm", optional: false]}], "hexpm"},
+ "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
+ "markdown": {:git, "https://github.com/devinus/markdown.git", "d065dbcc4e242a85ca2516fdadd0082712871fd8", []},
+ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [], [], "hexpm"},
+ "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [], [], "hexpm"},
+ "mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [], [], "hexpm"},
+ "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"},
+ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"},
+ "tzdata": {:hex, :tzdata, "0.5.12", "1c17b68692c6ba5b6ab15db3d64cc8baa0f182043d5ae9d4b6d35d70af76f67b", [], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
+ "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "hexpm"}}