Regenerate Drive client (#1239)

Regenerate Drive client (#1239)

# Copyright 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# NOTE: This class is auto generated by the swagger code generator program.
# https://github.com/swagger-api/swagger-codegen.git
# Do not edit the class manually.

defmodule GoogleApi.Drive.V3.Api.Permissions do
  @moduledoc """
  API calls for all endpoints tagged `Permissions`.
  """

  alias GoogleApi.Drive.V3.Connection
  alias GoogleApi.Gax.{Request, Response}

  @doc """
  Creates a permission for a file or Team Drive.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :emailMessage (String.t): A plain text custom message to include in the notification email.
    - :sendNotificationEmail (boolean()): Whether to send a notification email when sharing to users or groups. This defaults to true for users and groups, and is not allowed for other requests. It must not be disabled for ownership transfers.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :transferOwnership (boolean()): Whether to transfer ownership to the specified user and downgrade the current owner to a writer. This parameter is required as an acknowledgement of the side effect.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if the file ID parameter refers to a Team Drive and the requester is an administrator of the domain to which the Team Drive belongs.
    - :body (Permission):

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.Permission{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_create(Tesla.Env.client(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.Permission.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_create(connection, file_id, optional_params \\ [], opts \\ []) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :emailMessage => :query,
      :sendNotificationEmail => :query,
      :supportsTeamDrives => :query,
      :transferOwnership => :query,
      :useDomainAdminAccess => :query,
      :body => :body
    }

    request =
      Request.new()
      |> Request.method(:post)
      |> Request.url("/drive/v3/files/{fileId}/permissions", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.Permission{}])
  end

  @doc """
  Deletes a permission.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - permission_id (String.t): The ID of the permission.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if the file ID parameter refers to a Team Drive and the requester is an administrator of the domain to which the Team Drive belongs.

  ## Returns

  {:ok, %{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_delete(Tesla.Env.client(), String.t(), String.t(), keyword()) ::
          {:ok, nil} | {:error, Tesla.Env.t()}
  def drive_permissions_delete(
        connection,
        file_id,
        permission_id,
        optional_params \\ [],
        opts \\ []
      ) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :supportsTeamDrives => :query,
      :useDomainAdminAccess => :query
    }

    request =
      Request.new()
      |> Request.method(:delete)
      |> Request.url("/drive/v3/files/{fileId}/permissions/{permissionId}", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1),
        "permissionId" => URI.encode(permission_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [decode: false])
  end

  @doc """
  Gets a permission by ID.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file.
  - permission_id (String.t): The ID of the permission.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if the file ID parameter refers to a Team Drive and the requester is an administrator of the domain to which the Team Drive belongs.

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.Permission{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_get(Tesla.Env.client(), String.t(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.Permission.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_get(connection, file_id, permission_id, optional_params \\ [], opts \\ []) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :supportsTeamDrives => :query,
      :useDomainAdminAccess => :query
    }

    request =
      Request.new()
      |> Request.method(:get)
      |> Request.url("/drive/v3/files/{fileId}/permissions/{permissionId}", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1),
        "permissionId" => URI.encode(permission_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.Permission{}])
  end

  @doc """
  Lists a file's or Team Drive's permissions.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :pageSize (integer()): The maximum number of permissions to return per page. When not set for files in a Team Drive, at most 100 results will be returned. When not set for files that are not in a Team Drive, the entire list will be returned.
    - :pageToken (String.t): The token for continuing a previous list request on the next page. This should be set to the value of 'nextPageToken' from the previous response.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if the file ID parameter refers to a Team Drive and the requester is an administrator of the domain to which the Team Drive belongs.

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.PermissionList{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_list(Tesla.Env.client(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.PermissionList.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_list(connection, file_id, optional_params \\ [], opts \\ []) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :pageSize => :query,
      :pageToken => :query,
      :supportsTeamDrives => :query,
      :useDomainAdminAccess => :query
    }

    request =
      Request.new()
      |> Request.method(:get)
      |> Request.url("/drive/v3/files/{fileId}/permissions", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.PermissionList{}])
  end

  @doc """
  Updates a permission with patch semantics.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - permission_id (String.t): The ID of the permission.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :removeExpiration (boolean()): Whether to remove the expiration date.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :transferOwnership (boolean()): Whether to transfer ownership to the specified user and downgrade the current owner to a writer. This parameter is required as an acknowledgement of the side effect.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if the file ID parameter refers to a Team Drive and the requester is an administrator of the domain to which the Team Drive belongs.
    - :body (Permission):

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.Permission{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_update(Tesla.Env.client(), String.t(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.Permission.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_update(
        connection,
        file_id,
        permission_id,
        optional_params \\ [],
        opts \\ []
      ) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :removeExpiration => :query,
      :supportsTeamDrives => :query,
      :transferOwnership => :query,
      :useDomainAdminAccess => :query,
      :body => :body
    }

    request =
      Request.new()
      |> Request.method(:patch)
      |> Request.url("/drive/v3/files/{fileId}/permissions/{permissionId}", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1),
        "permissionId" => URI.encode(permission_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.Permission{}])
  end
end

# Copyright 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# NOTE: This class is auto generated by the swagger code generator program.
# https://github.com/swagger-api/swagger-codegen.git
# Do not edit the class manually.

defmodule GoogleApi.Drive.V3.Api.Permissions do
  @moduledoc """
  API calls for all endpoints tagged `Permissions`.
  """

  alias GoogleApi.Drive.V3.Connection
  alias GoogleApi.Gax.{Request, Response}

  @doc """
  Creates a permission for a file or Team Drive.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :emailMessage (String.t): A plain text custom message to include in the notification email.
    - :sendNotificationEmail (boolean()): Whether to send a notification email when sharing to users or groups. This defaults to true for users and groups, and is not allowed for other requests. It must not be disabled for ownership transfers.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :transferOwnership (boolean()): Whether to transfer ownership to the specified user and downgrade the current owner to a writer. This parameter is required as an acknowledgement of the side effect.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the item belongs.
    - :body (Permission):

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.Permission{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_create(Tesla.Env.client(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.Permission.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_create(connection, file_id, optional_params \\ [], opts \\ []) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :emailMessage => :query,
      :sendNotificationEmail => :query,
      :supportsTeamDrives => :query,
      :transferOwnership => :query,
      :useDomainAdminAccess => :query,
      :body => :body
    }

    request =
      Request.new()
      |> Request.method(:post)
      |> Request.url("/drive/v3/files/{fileId}/permissions", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.Permission{}])
  end

  @doc """
  Deletes a permission.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - permission_id (String.t): The ID of the permission.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the item belongs.

  ## Returns

  {:ok, %{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_delete(Tesla.Env.client(), String.t(), String.t(), keyword()) ::
          {:ok, nil} | {:error, Tesla.Env.t()}
  def drive_permissions_delete(
        connection,
        file_id,
        permission_id,
        optional_params \\ [],
        opts \\ []
      ) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :supportsTeamDrives => :query,
      :useDomainAdminAccess => :query
    }

    request =
      Request.new()
      |> Request.method(:delete)
      |> Request.url("/drive/v3/files/{fileId}/permissions/{permissionId}", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1),
        "permissionId" => URI.encode(permission_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [decode: false])
  end

  @doc """
  Gets a permission by ID.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file.
  - permission_id (String.t): The ID of the permission.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the item belongs.

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.Permission{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_get(Tesla.Env.client(), String.t(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.Permission.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_get(connection, file_id, permission_id, optional_params \\ [], opts \\ []) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :supportsTeamDrives => :query,
      :useDomainAdminAccess => :query
    }

    request =
      Request.new()
      |> Request.method(:get)
      |> Request.url("/drive/v3/files/{fileId}/permissions/{permissionId}", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1),
        "permissionId" => URI.encode(permission_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.Permission{}])
  end

  @doc """
  Lists a file's or Team Drive's permissions.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :pageSize (integer()): The maximum number of permissions to return per page. When not set for files in a Team Drive, at most 100 results will be returned. When not set for files that are not in a Team Drive, the entire list will be returned.
    - :pageToken (String.t): The token for continuing a previous list request on the next page. This should be set to the value of 'nextPageToken' from the previous response.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the item belongs.

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.PermissionList{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_list(Tesla.Env.client(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.PermissionList.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_list(connection, file_id, optional_params \\ [], opts \\ []) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :pageSize => :query,
      :pageToken => :query,
      :supportsTeamDrives => :query,
      :useDomainAdminAccess => :query
    }

    request =
      Request.new()
      |> Request.method(:get)
      |> Request.url("/drive/v3/files/{fileId}/permissions", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.PermissionList{}])
  end

  @doc """
  Updates a permission with patch semantics.

  ## Parameters

  - connection (GoogleApi.Drive.V3.Connection): Connection to server
  - file_id (String.t): The ID of the file or Team Drive.
  - permission_id (String.t): The ID of the permission.
  - optional_params (KeywordList): [optional] Optional parameters
    - :alt (String.t): Data format for the response.
    - :fields (String.t): Selector specifying which fields to include in a partial response.
    - :key (String.t): API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
    - :oauth_token (String.t): OAuth 2.0 token for the current user.
    - :prettyPrint (boolean()): Returns response with indentations and line breaks.
    - :quotaUser (String.t): An opaque string that represents a user for quota purposes. Must not exceed 40 characters.
    - :userIp (String.t): Deprecated. Please use quotaUser instead.
    - :removeExpiration (boolean()): Whether to remove the expiration date.
    - :supportsTeamDrives (boolean()): Whether the requesting application supports Team Drives.
    - :transferOwnership (boolean()): Whether to transfer ownership to the specified user and downgrade the current owner to a writer. This parameter is required as an acknowledgement of the side effect.
    - :useDomainAdminAccess (boolean()): Issue the request as a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the item belongs.
    - :body (Permission):

  ## Returns

  {:ok, %GoogleApi.Drive.V3.Model.Permission{}} on success
  {:error, info} on failure
  """
  @spec drive_permissions_update(Tesla.Env.client(), String.t(), String.t(), keyword()) ::
          {:ok, GoogleApi.Drive.V3.Model.Permission.t()} | {:error, Tesla.Env.t()}
  def drive_permissions_update(
        connection,
        file_id,
        permission_id,
        optional_params \\ [],
        opts \\ []
      ) do
    optional_params_config = %{
      :alt => :query,
      :fields => :query,
      :key => :query,
      :oauth_token => :query,
      :prettyPrint => :query,
      :quotaUser => :query,
      :userIp => :query,
      :removeExpiration => :query,
      :supportsTeamDrives => :query,
      :transferOwnership => :query,
      :useDomainAdminAccess => :query,
      :body => :body
    }

    request =
      Request.new()
      |> Request.method(:patch)
      |> Request.url("/drive/v3/files/{fileId}/permissions/{permissionId}", %{
        "fileId" => URI.encode(file_id, &URI.char_unreserved?/1),
        "permissionId" => URI.encode(permission_id, &URI.char_unreserved?/1)
      })
      |> Request.add_optional_params(optional_params_config, optional_params)

    connection
    |> Connection.execute(request)
    |> Response.decode(opts ++ [struct: %GoogleApi.Drive.V3.Model.Permission{}])
  end
end


Easily allow for local workers during development time

# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# Set the coordinator node to unknown to start it locally
config :baumeister_core, coordinator_node: :unknown


fixing wron resource_collection_name in ExTwilio.Memeber

defmodule ExTwilio.Member do
  @moduledoc """
  Represents an Member resource in the Twilio API.

  - [Twilio docs](https://www.twilio.com/docs/api/rest/members)

  ## Examples

  Since Members are members of a Queue in the Twilio API, you must pass a Queue
  SID into each function in this module.

      ExTwilio.Member.all(queue: "queue_sid")
  """

  defstruct call_sid: nil,
            date_enqueued: nil,
            wait_time: nil,
            position: nil

  use ExTwilio.Resource, import: [:stream, :all, :find, :update]

  def parents, do: [:account, :queue]

  def resource_collection_name(), do: Url.resource_collection_name(ExTwilio.QueueMember)
end

defmodule ExTwilio.Member do
  @moduledoc """
  Represents an Member resource in the Twilio API.

  - [Twilio docs](https://www.twilio.com/docs/api/rest/members)

  ## Examples

  Since Members are members of a Queue in the Twilio API, you must pass a Queue
  SID into each function in this module.

      ExTwilio.Member.all(queue: "queue_sid")
  """

  defstruct call_sid: nil,
            date_enqueued: nil,
            wait_time: nil,
            position: nil

  use ExTwilio.Resource, import: [:stream, :all, :find, :update]

  def parents, do: [:account, :queue]
end


Add debug function to farm_event manager

defmodule Farmbot.FarmEvent.Manager do
  @moduledoc """
  Manages execution of FarmEvents.

  ## Rules for FarmEvent execution.
  * Regimen
    * ignore `end_time`.
    * ignore calendar.
    * if start_time is more than 60 seconds passed due, assume it already started, and don't start it again.
  * Sequence
    * if `start_time` is late, check the calendar.
      * for each item in the calendar, check if it's event is more than 60 seconds in the past. if not, execute it.
    * if there is only one event in the calendar, ignore the `end_time`
  """

  # credo:disable-for-this-file Credo.Check.Refactor.FunctionArity

  use GenServer
  use Farmbot.Logger
  alias Farmbot.FarmEvent.Execution
  alias Farmbot.Repo.FarmEvent

  # @checkup_time 100
  @checkup_time 30_000

  def register_events(event_list) do
    GenServer.call(__MODULE__, {:register_events, event_list}, 10_000)
  end

  ## GenServer

  defmodule State do
    @moduledoc false
    defstruct [timer: nil, last_time_index: %{}, events: [], checkup: nil]
  end

  @doc false
  def start_link do
    GenServer.start_link(__MODULE__, [], [name: __MODULE__])
  end

  def init([]) do
    send self(), :checkup
    {:ok, struct(State)}
  end

  def terminate(reason, _state) do
    Logger.error 1, "FarmEvent Manager terminated: #{inspect reason}"
  end

  def handle_call({:register_events, events}, _, state) do
    maybe_farm_event_log "Reindexed FarmEvents"
    if match?({_, _}, state.checkup) do
      Process.exit(state.checkup |> elem(0), {:success, %{state | events: events}})
    end

    if state.timer do
      Process.cancel_timer(state.timer)
      timer = Process.send_after(self(), :checkup, @checkup_time)
      {:reply, :ok, %{state | events: events, timer: timer}}
    else
      {:reply, :ok, %{state | events: events}}
    end
  end

  def handle_info(:checkup, state) do
    checkup = spawn_monitor __MODULE__, :async_checkup, [self(), state]
    {:noreply, %{state | timer: nil, checkup: checkup}}
  end

  def handle_info({:DOWN, _, :process, _, {:success, new_state}}, old_state) do
    timer = Process.send_after(self(), :checkup, @checkup_time)
    {:noreply, %{new_state | timer: timer, checkup: nil}}
  end

  def handle_info({:DOWN, _, :process, _, error}, state) do
    Logger.error 1, "Farmevent checkup process died: #{inspect error}"
    timer = Process.send_after(self(), :checkup, @checkup_time)
    {:noreply, %{state | timer: timer, checkup: nil}}
  end

  def async_checkup(manager, state) do
    now = get_now()
    alias Farmbot.Repo.FarmEvent
    # maybe_farm_event_log "Rebuilding calendar."
    all_events = state.events |> Enum.map(&FarmEvent.build_calendar(&1))
    # maybe_farm_event_log "Rebuilding calendar complete."

    # do checkup is the bulk of the work.
    {late_events, new} = do_checkup(all_events, now, state)

    #TODO(Connor) Conditionally start events based on some state info.
    unless Enum.empty?(late_events) do
      Logger.info 3, "Time for events: #{inspect late_events} to run at: #{now.hour}:#{now.minute}"
      start_events(late_events, now)
    end
    exit({:success, new})
  end

  defp do_checkup(list, time, late_events \\ [], state)

  defp do_checkup([], _now, late_events, state), do: {late_events, state}

  defp do_checkup([farm_event | rest], now, late_events, state) do
    # new_late will be a executable event (Regimen or Sequence.)
    {new_late_event, last_time} = check_event(farm_event, now, state.last_time_index[farm_event.id])

    # update state.
    new_state = %{state | last_time_index: Map.put(state.last_time_index, farm_event.id, last_time)}
    case new_late_event do
      # if `new_late_event` is nil, don't accumulate it.
      nil   -> do_checkup(rest, now, late_events, new_state)
      # if there is a new event, accumulate it.
      event -> do_checkup(rest, now, [event | late_events], new_state)
    end
  end

  defp check_event(%FarmEvent{} = f, now, last_time) do
    # Get the executable out of the database this may fail.
    # mod_list = ["Farmbot", "Repo", f.executable_type]
    mod      = Module.safe_concat([f.executable_type])

    event    = lookup(mod, f.executable_id)

    # build a local start time and end time
    start_time = Timex.parse! f.start_time, "{ISO:Extended}"
    end_time   = Timex.parse! f.end_time,   "{ISO:Extended}"
    # start_time = f.start_time
    # end_time = f.end_time
    # get local bool of if the event is started and finished.
    started?  = Timex.after? now, start_time
    finished? = Timex.after? now, end_time

    case f.executable_type do
      "Elixir.Farmbot.Repo.Regimen"  -> maybe_start_regimen(started?, start_time, last_time, event, now)
      "Elixir.Farmbot.Repo.Sequence" -> maybe_start_sequence(started?, finished?, f, last_time, event, now)
    end
  end

  defp maybe_start_regimen(started?, start_time, last_time, event, now)
  defp maybe_start_regimen(true = _started?, start_time, last_time, event, now) do
    case is_too_old?(now, start_time) do
      true  ->
        maybe_farm_event_log "regimen #{event.name} (#{event.id}) is too old to start or already started."
        {nil, last_time}
      false ->
        maybe_farm_event_log "regimen #{event.name} (#{event.id}) starting."
        {event, now}
    end
  end

  defp maybe_start_regimen(false = _started?, start_time, last_time, event, _) do
    maybe_farm_event_log "regimen #{event.name} (#{event.id}) is not started yet. (#{inspect start_time}) (#{inspect Timex.now()})"
    {nil, last_time}
  end

  defp lookup(module, sr_id) do
    case Farmbot.Repo.current_repo().get(module, sr_id) do
      nil -> raise "Could not find #{module} by id: #{sr_id}"
      item -> item
    end
  end

  # signals the start of a sequence based on the described logic.
  defp maybe_start_sequence(started?, finished?, farm_event, last_time, event, now)

  # We only want to check if the sequence is started, and not finished.
  defp maybe_start_sequence(true = _started?, false = _finished?, farm_event, last_time, event, now) do
    {run?, next_time} = should_run_sequence?(farm_event.calendar, last_time, now)
    case run? do
      true  -> {event, next_time}
      false -> {nil, last_time}
    end
  end

  # if `farm_event.time_unit` is "never" we can't use the `end_time`.
  # if we have no `last_time`, time to execute.
  defp maybe_start_sequence(true = _started?, _, %{time_unit: "never"} = f, nil = _last_time, event, now) do
    maybe_farm_event_log "Ignoring end_time."
    case should_run_sequence?(f.calendar, nil, now) do
      {true, next} -> {event, next}
      {false,   _} -> {nil,    nil}
    end
  end

  # if started is false, the event isn't ready to be executed.
  defp maybe_start_sequence(false = _started?, _fin, _farm_event, last_time, event, _now) do
    maybe_farm_event_log "sequence #{event.name} (#{event.id}) is not started yet."
    {nil, last_time}
  end

  # if the event is finished (but not a "never" time_unit), we don't execute.
  defp maybe_start_sequence(_started?, true = _finished?, _farm_event, last_time, event, _now) do
    Logger.success 3, "sequence #{event.name} (#{event.id}) is finished."
    {nil, last_time}
  end

  # Checks  if we shoudl run a sequence or not. returns {event | nil, time | nil}
  defp should_run_sequence?(calendar, last_time, now)

  # if there is no last time, check if time is passed now within 60 seconds.
  defp should_run_sequence?([first_time | _], nil, now) do

    maybe_farm_event_log "Checking sequence event that hasn't run before #{first_time}"
    # convert the first_time to a DateTime
    dt = Timex.parse! first_time, "{ISO:Extended}"
    # if now is after the time, we are in fact late
    if Timex.after?(now, dt) do
        {true, now}
     else
       # make sure to return nil as the last time because it stil hasnt executed yet.
       maybe_farm_event_log "Sequence Event not ready yet."
      {false, nil}
    end
  end

  defp should_run_sequence?(nil, last_time, now) do
    maybe_farm_event_log "Checking sequence with no calendar."
    if is_nil(last_time) do
      {true, now}
    else
      {false, last_time}
    end
  end

  defp should_run_sequence?(calendar, last_time, now) do
    # get rid of all the items that happened before last_time
    filtered_calendar = Enum.filter(calendar, fn(iso_time) ->
      dt = Timex.parse! iso_time, "{ISO:Extended}"
      # we only want this time if it happened after the last_time
      Timex.after?(dt, last_time)
    end)

    # if after filtering, there are events that need to be run
    # check if they are older than a minute ago,
    case filtered_calendar do
      [iso_time |  _] ->
        dt = Timex.parse! iso_time, "{ISO:Extended}"
        if Timex.after?(now, dt) do
          {true, dt}
          # too_old? = is_too_old?(now, dt)
          # if too_old?, do: {false, last_time}, else: {true, dt}
        else
          maybe_farm_event_log "Sequence Event not ready yet."
          {false, dt}
        end
      [] ->
        maybe_farm_event_log "No items in calendar."
        {false, last_time}
    end
  end

  # Enumeration is complete.
  defp start_events([], _now), do: :ok

  # Enumerate the events to be started.
  defp start_events([event | rest], now) do
    # Spawn to be non blocking here. Maybe link to this process?
    spawn fn() -> Execution.execute_event(event, now) end
    # Continue enumeration.
    start_events(rest, now)
  end

  # is then more than 1 minute in the past?
  defp is_too_old?(now, then) do
    time_str_fun = fn(dt) -> "#{dt.hour}:#{dt.minute}:#{dt.second}" end
    seconds = DateTime.to_unix(now, :second) - DateTime.to_unix(then, :second)
    c = seconds > 60 # not in MS here
    maybe_farm_event_log "is checking #{time_str_fun.(now)} - #{time_str_fun.(then)} = #{seconds} seconds ago. is_too_old? => #{c}"
    c
  end

  defp get_now(), do: Timex.now()

  defp maybe_farm_event_log(message) do
    if Application.get_env(:farmbot, :farm_event_debug_log) do
      Logger.debug 3, message
    else
      :ok
    end
  end

  @doc "Enable or disbale debug logs for farmevents."
  def debug_logs(bool \\ true) when is_boolean(bool) do
    Application.put_env(:farmbot, :farm_event_debug_log, bool)
  end
end

defmodule Farmbot.FarmEvent.Manager do
  @moduledoc """
  Manages execution of FarmEvents.

  ## Rules for FarmEvent execution.
  * Regimen
    * ignore `end_time`.
    * ignore calendar.
    * if start_time is more than 60 seconds passed due, assume it already started, and don't start it again.
  * Sequence
    * if `start_time` is late, check the calendar.
      * for each item in the calendar, check if it's event is more than 60 seconds in the past. if not, execute it.
    * if there is only one event in the calendar, ignore the `end_time`
  """

  # credo:disable-for-this-file Credo.Check.Refactor.FunctionArity

  use GenServer
  use Farmbot.Logger
  alias Farmbot.FarmEvent.Execution
  alias Farmbot.Repo.FarmEvent

  # @checkup_time 100
  @checkup_time 30_000

  def register_events(event_list) do
    GenServer.call(__MODULE__, {:register_events, event_list}, 10_000)
  end

  ## GenServer

  defmodule State do
    @moduledoc false
    defstruct [timer: nil, last_time_index: %{}, events: [], checkup: nil]
  end

  @doc false
  def start_link do
    GenServer.start_link(__MODULE__, [], [name: __MODULE__])
  end

  def init([]) do
    send self(), :checkup
    {:ok, struct(State)}
  end

  def terminate(reason, _state) do
    Logger.error 1, "FarmEvent Manager terminated: #{inspect reason}"
  end

  def handle_call({:register_events, events}, _, state) do
    maybe_farm_event_log "Reindexed FarmEvents"
    if match?({_, _}, state.checkup) do
      Process.exit(state.checkup |> elem(0), {:success, %{state | events: events}})
    end

    if state.timer do
      Process.cancel_timer(state.timer)
      timer = Process.send_after(self(), :checkup, @checkup_time)
      {:reply, :ok, %{state | events: events, timer: timer}}
    else
      {:reply, :ok, %{state | events: events}}
    end
  end

  def handle_info(:checkup, state) do
    checkup = spawn_monitor __MODULE__, :async_checkup, [self(), state]
    {:noreply, %{state | timer: nil, checkup: checkup}}
  end

  def handle_info({:DOWN, _, :process, _, {:success, new_state}}, old_state) do
    timer = Process.send_after(self(), :checkup, @checkup_time)
    {:noreply, %{new_state | timer: timer, checkup: nil}}
  end

  def handle_info({:DOWN, _, :process, _, error}, state) do
    Logger.error 1, "Farmevent checkup process died: #{inspect error}"
    timer = Process.send_after(self(), :checkup, @checkup_time)
    {:noreply, %{state | timer: timer, checkup: nil}}
  end

  def async_checkup(manager, state) do
    now = get_now()
    alias Farmbot.Repo.FarmEvent
    # maybe_farm_event_log "Rebuilding calendar."
    all_events = state.events |> Enum.map(&FarmEvent.build_calendar(&1))
    # maybe_farm_event_log "Rebuilding calendar complete."

    # do checkup is the bulk of the work.
    {late_events, new} = do_checkup(all_events, now, state)

    #TODO(Connor) Conditionally start events based on some state info.
    unless Enum.empty?(late_events) do
      Logger.info 3, "Time for events: #{inspect late_events} to run at: #{now.hour}:#{now.minute}"
      start_events(late_events, now)
    end
    exit({:success, new})
  end

  defp do_checkup(list, time, late_events \\ [], state)

  defp do_checkup([], _now, late_events, state), do: {late_events, state}

  defp do_checkup([farm_event | rest], now, late_events, state) do
    # new_late will be a executable event (Regimen or Sequence.)
    {new_late_event, last_time} = check_event(farm_event, now, state.last_time_index[farm_event.id])

    # update state.
    new_state = %{state | last_time_index: Map.put(state.last_time_index, farm_event.id, last_time)}
    case new_late_event do
      # if `new_late_event` is nil, don't accumulate it.
      nil   -> do_checkup(rest, now, late_events, new_state)
      # if there is a new event, accumulate it.
      event -> do_checkup(rest, now, [event | late_events], new_state)
    end
  end

  defp check_event(%FarmEvent{} = f, now, last_time) do
    # Get the executable out of the database this may fail.
    # mod_list = ["Farmbot", "Repo", f.executable_type]
    mod      = Module.safe_concat([f.executable_type])

    event    = lookup(mod, f.executable_id)

    # build a local start time and end time
    start_time = Timex.parse! f.start_time, "{ISO:Extended}"
    end_time   = Timex.parse! f.end_time,   "{ISO:Extended}"
    # start_time = f.start_time
    # end_time = f.end_time
    # get local bool of if the event is started and finished.
    started?  = Timex.after? now, start_time
    finished? = Timex.after? now, end_time

    case f.executable_type do
      "Elixir.Farmbot.Repo.Regimen"  -> maybe_start_regimen(started?, start_time, last_time, event, now)
      "Elixir.Farmbot.Repo.Sequence" -> maybe_start_sequence(started?, finished?, f, last_time, event, now)
    end
  end

  defp maybe_start_regimen(started?, start_time, last_time, event, now)
  defp maybe_start_regimen(true = _started?, start_time, last_time, event, now) do
    case is_too_old?(now, start_time) do
      true  ->
        maybe_farm_event_log "regimen #{event.name} (#{event.id}) is too old to start or already started."
        {nil, last_time}
      false ->
        maybe_farm_event_log "regimen #{event.name} (#{event.id}) starting."
        {event, now}
    end
  end

  defp maybe_start_regimen(false = _started?, start_time, last_time, event, _) do
    maybe_farm_event_log "regimen #{event.name} (#{event.id}) is not started yet. (#{inspect start_time}) (#{inspect Timex.now()})"
    {nil, last_time}
  end

  defp lookup(module, sr_id) do
    case Farmbot.Repo.current_repo().get(module, sr_id) do
      nil -> raise "Could not find #{module} by id: #{sr_id}"
      item -> item
    end
  end

  # signals the start of a sequence based on the described logic.
  defp maybe_start_sequence(started?, finished?, farm_event, last_time, event, now)

  # We only want to check if the sequence is started, and not finished.
  defp maybe_start_sequence(true = _started?, false = _finished?, farm_event, last_time, event, now) do
    {run?, next_time} = should_run_sequence?(farm_event.calendar, last_time, now)
    case run? do
      true  -> {event, next_time}
      false -> {nil, last_time}
    end
  end

  # if `farm_event.time_unit` is "never" we can't use the `end_time`.
  # if we have no `last_time`, time to execute.
  defp maybe_start_sequence(true = _started?, _, %{time_unit: "never"} = f, nil = _last_time, event, now) do
    maybe_farm_event_log "Ignoring end_time."
    case should_run_sequence?(f.calendar, nil, now) do
      {true, next} -> {event, next}
      {false,   _} -> {nil,    nil}
    end
  end

  # if started is false, the event isn't ready to be executed.
  defp maybe_start_sequence(false = _started?, _fin, _farm_event, last_time, event, _now) do
    maybe_farm_event_log "sequence #{event.name} (#{event.id}) is not started yet."
    {nil, last_time}
  end

  # if the event is finished (but not a "never" time_unit), we don't execute.
  defp maybe_start_sequence(_started?, true = _finished?, _farm_event, last_time, event, _now) do
    Logger.success 3, "sequence #{event.name} (#{event.id}) is finished."
    {nil, last_time}
  end

  # Checks  if we shoudl run a sequence or not. returns {event | nil, time | nil}
  defp should_run_sequence?(calendar, last_time, now)

  # if there is no last time, check if time is passed now within 60 seconds.
  defp should_run_sequence?([first_time | _], nil, now) do

    maybe_farm_event_log "Checking sequence event that hasn't run before #{first_time}"
    # convert the first_time to a DateTime
    dt = Timex.parse! first_time, "{ISO:Extended}"
    # if now is after the time, we are in fact late
    if Timex.after?(now, dt) do
        {true, now}
     else
       # make sure to return nil as the last time because it stil hasnt executed yet.
       maybe_farm_event_log "Sequence Event not ready yet."
      {false, nil}
    end
  end

  defp should_run_sequence?(nil, last_time, now) do
    maybe_farm_event_log "Checking sequence with no calendar."
    if is_nil(last_time) do
      {true, now}
    else
      {false, last_time}
    end
  end

  defp should_run_sequence?(calendar, last_time, now) do
    # get rid of all the items that happened before last_time
    filtered_calendar = Enum.filter(calendar, fn(iso_time) ->
      dt = Timex.parse! iso_time, "{ISO:Extended}"
      # we only want this time if it happened after the last_time
      Timex.after?(dt, last_time)
    end)

    # if after filtering, there are events that need to be run
    # check if they are older than a minute ago,
    case filtered_calendar do
      [iso_time |  _] ->
        dt = Timex.parse! iso_time, "{ISO:Extended}"
        if Timex.after?(now, dt) do
          {true, dt}
          # too_old? = is_too_old?(now, dt)
          # if too_old?, do: {false, last_time}, else: {true, dt}
        else
          maybe_farm_event_log "Sequence Event not ready yet."
          {false, dt}
        end
      [] ->
        maybe_farm_event_log "No items in calendar."
        {false, last_time}
    end
  end

  # Enumeration is complete.
  defp start_events([], _now), do: :ok

  # Enumerate the events to be started.
  defp start_events([event | rest], now) do
    # Spawn to be non blocking here. Maybe link to this process?
    spawn fn() -> Execution.execute_event(event, now) end
    # Continue enumeration.
    start_events(rest, now)
  end

  # is then more than 1 minute in the past?
  defp is_too_old?(now, then) do
    time_str_fun = fn(dt) -> "#{dt.hour}:#{dt.minute}:#{dt.second}" end
    seconds = DateTime.to_unix(now, :second) - DateTime.to_unix(then, :second)
    c = seconds > 60 # not in MS here
    maybe_farm_event_log "is checking #{time_str_fun.(now)} - #{time_str_fun.(then)} = #{seconds} seconds ago. is_too_old? => #{c}"
    c
  end

  defp get_now(), do: Timex.now()

  defp maybe_farm_event_log(message) do
    if Application.get_env(:farmbot, :farm_event_debug_log) do
      Logger.debug 3, message
    else
      :ok
    end
  end
end


cleanup mix

cleanup mix

defmodule DB.Mixfile do
  use Mix.Project

  def project do
    [app: :db,
     version: PerhapAPI.Mixfile.version,
     elixir: "~> 1.4",
     build_path: "../../_build",
     config_path: "../../config/config.exs",
     deps_path: "../../deps",
     lockfile: "../../mix.lock",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  def application do
    [
      applications: [
        :logger,
        :tzdata,
        :riak],
        # mod: {DB, []}
    ]
  end

  defp deps do
    [
      {:tzdata, "~> 0.5.12"},
      {:geocalc, "~> 0.5.4"},
      {:riak, "~> 1.1.6"},
      {:timex, "~> 3.1"},
      {:uuid, github: "okeuday/uuid"}
    ]
  end
end

defmodule DB.Mixfile do
  use Mix.Project

  def project do
    [app: :db,
     version: PerhapAPI.Mixfile.version,
     elixir: "~> 1.4",
     build_path: "../../_build",
     config_path: "../../config/config.exs",
     deps_path: "../../deps",
     lockfile: "../../mix.lock",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  def application do
    [
      applications: [
        :logger,
        :tzdata,
        :riak],
        #:phoenix_pubsub],
        # mod: {DB, []}
    ]
  end

  defp deps do
    [
      {:tzdata, "~> 0.5.12"},
      {:geocalc, "~> 0.5.4"},
      {:riak, "~> 1.1.4"},
      {:timex, "~> 3.1"},
      # {:tzdata, "== 0.1.8", override: true},
      # {:phoenix_pubsub, "~> 1.0"},
      {:uuid, github: "okeuday/uuid"}
    ]
  end
end


Add tests for keyword lists in @lint tuples

defmodule Credo.Check.LintAttributeTest do
  use Credo.TestHelper

  alias Credo.Check.LintAttribute
  alias Credo.Issue

  # Asserts that a given `source` produces the `expected` value.
  defp assert_value(source, expected) do
    input = source |> Code.string_to_quoted!
    lint_attribute = LintAttribute.from_ast(input)
    assert expected == lint_attribute.value, "Expected #{inspect(expected)}, got #{inspect(lint_attribute.value)}"
  end

  test "it should return " do
    assert_value "@lint false",
                        false
  end

  test "it should work for a tuple" do
    assert_value "@lint {Credo.Check.Design.AliasUsage, false}",
                        [{Credo.Check.Design.AliasUsage, false}]
  end

  test "it should work for a Regex" do
    assert_value "@lint {~r/Refactor/, false}",
                        [{~r/Refactor/, false}]
  end

  test "it should work for a Regex ~R" do
    assert_value "@lint {~R/Refactor/, false}",
                        [{~R/Refactor/, false}]
  end

  test "it should work for a list of tuples" do
    assert_value "@lint [{Credo.Check.Design.AliasUsage, false}]",
                        [{Credo.Check.Design.AliasUsage, false}]
  end

  test "it should work for a list of Regexes" do
    assert_value "@lint [{~r/Refactor/, false}]",
                        [{~r/Refactor/, false}]
  end

  test "it should work for a tuple, followed by a custom params list" do
    assert_value "@lint {Credo.Check.Design.AliasUsage, a: 1, b: 2}",
                        [{Credo.Check.Design.AliasUsage, a: 1, b: 2}]
  end

  test "it should work for a Regex, followed by a custom params list" do
    assert_value "@lint {~r/Refactor/, a: 1, b: 2}",
                        [{~r/Refactor/, a: 1, b: 2}]
  end

  test "it should work for a Regex ~R, followed by a custom params list" do
    assert_value "@lint {~R/Refactor/, a: 1, b: 2}",
                        [{~R/Refactor/, a: 1, b: 2}]
  end

  test "it should work for a list of tuples, followed by a custom params list" do
    assert_value "@lint [{Credo.Check.Design.AliasUsage, a: 1, b: 2}]",
                        [{Credo.Check.Design.AliasUsage, a: 1, b: 2}]
  end

  test "it should work for a list of Regexes, followed by a custom params list" do
    assert_value "@lint [{~r/Refactor/, a: 1, b: 2}]",
                        [{~r/Refactor/, a: 1, b: 2}]
  end

  test "it should return true for @lint false with same scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: false, scope: "MyScope.fun"}
    assert LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint false with other scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: false, scope: "MyScope.fun2"}
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return true for @lint [{check, false}] with same scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: [{Credo.Check.Refactor.ABCSize, false}], scope: "MyScope.fun"}
    assert LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{check, false}] with other scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{Credo.Check.Refactor.ABCSize, false}],
        scope: "MyScope.other_fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{check, false}] with other check" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{Credo.Check.Refactor.CyclomaticComplexity, false}],
        scope: "MyScope.fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return true for @lint [{~r//, false}] with same scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: [{~r/Refactor/, false}], scope: "MyScope.fun"}
    assert LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{~r//, false}] with other scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{~r/Refactor/, false}],
        scope: "MyScope.other_fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{~r//, false}] with other check" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{~r/Readability/, false}],
        scope: "MyScope.fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

end

defmodule Credo.Check.LintAttributeTest do
  use Credo.TestHelper

  alias Credo.Check.LintAttribute
  alias Credo.Issue

  # Asserts that a given `source` produces the `expected` value.
  defp assert_value(source, expected) do
    input = source |> Code.string_to_quoted!
    lint_attribute = LintAttribute.from_ast(input)
    assert expected == lint_attribute.value, "Expected #{inspect(expected)}, got #{inspect(lint_attribute.value)}"
  end

  test "it should return " do
    assert_value "@lint false",
                        false
  end

  test "it should work for a tuple" do
    assert_value "@lint {Credo.Check.Design.AliasUsage, false}",
                        [{Credo.Check.Design.AliasUsage, false}]
  end

  test "it should work for a Regex" do
    assert_value "@lint {~r/Refactor/, false}",
                        [{~r/Refactor/, false}]
  end

  test "it should work for a Regex ~R" do
    assert_value "@lint {~R/Refactor/, false}",
                        [{~R/Refactor/, false}]
  end

  test "it should work for a list of tuples" do
    assert_value "@lint [{Credo.Check.Design.AliasUsage, false}]",
                        [{Credo.Check.Design.AliasUsage, false}]
  end

  test "it should work for a list of Regexes" do
    assert_value "@lint [{~r/Refactor/, false}]",
                        [{~r/Refactor/, false}]
  end

  test "it should return true for @lint false with same scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: false, scope: "MyScope.fun"}
    assert LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint false with other scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: false, scope: "MyScope.fun2"}
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return true for @lint [{check, false}] with same scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: [{Credo.Check.Refactor.ABCSize, false}], scope: "MyScope.fun"}
    assert LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{check, false}] with other scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{Credo.Check.Refactor.ABCSize, false}],
        scope: "MyScope.other_fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{check, false}] with other check" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{Credo.Check.Refactor.CyclomaticComplexity, false}],
        scope: "MyScope.fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return true for @lint [{~r//, false}] with same scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute = %LintAttribute{value: [{~r/Refactor/, false}], scope: "MyScope.fun"}
    assert LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{~r//, false}] with other scope" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{~r/Refactor/, false}],
        scope: "MyScope.other_fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

  test "it should return false for @lint [{~r//, false}] with other check" do
    issue = %Issue{check: Credo.Check.Refactor.ABCSize, scope: "MyScope.fun"}
    lint_attribute =
      %LintAttribute{
        value: [{~r/Readability/, false}],
        scope: "MyScope.fun"
      }
    refute LintAttribute.ignores_issue?(lint_attribute, issue)
  end

end


Remove call

Remove call

Not used

defmodule Asteroid.Server do

   @moduledoc """
   Asteroid process. Asteroids have position, size and velocity,
   and wander the game area.
   """

   use GenServer

   alias World.Clock,    as: Clock
   alias World.Point,    as: Point
   alias World.Velocity, as: Velocity
   alias Elixoids.Space, as: Space

   # Radius of random asteroid
   @asteroid_radius_m     120.0

   # Smallest asteroid that can survive being hit
   @splittable_radius_m    20.0

   # 45º
   @quarter_pi_radians (:math.pi / 4.0)

   # Initial speed of asteroid
   @asteroid_speed_m_per_s 20.0

   def start_link(id, game_pid, asteroid \\ random_asteroid()) do
     a = Map.merge(asteroid, %{
      :id=>id,
      :game_pid=>game_pid,
      :clock_ms=>Clock.now_ms,
      :tick_ms=>Clock.ms_between_frames})

     GenServer.start_link(__MODULE__, a, [])
   end

   @doc """
   The asteroid has been destroyed.

      {:ok, a} = Asteroid.Server.start_link(9999)
      Process.alive?(a)
      Asteroid.Server.stop(a)
      Process.alive?(a)
   """
   def stop(pid) do
     GenServer.cast(pid, :stop)
   end

   @doc """
   Return a list of zero or two new asteroid states.

   Returns empty list if the asteroid is too small to be split.
   Otherwise returns a list of two new states of smaller rocks
   flying in opposite directions.

   {:ok, game} = Game.Server.start_link(60)
   Game.Server.show(game)

   rock = IEx.Helpers.pid(0,140,0)
   Asteroid.Server.split(rock)
   """
   def split(pid) do
     GenServer.call(pid, :split)
   end

   # GenServer callbacks

   def init(a) do
     Process.send(self(), :tick, [])
     {:ok, a}
   end

   def handle_cast(:move, a) do
     delta_t_ms = Clock.since(a.clock_ms)

     moved_asteroid = a
     |> move_asteroid(delta_t_ms)
     |> Map.put(:clock_ms, Clock.now_ms)

     Game.Server.update_asteroid(a.game_pid, state_tuple(moved_asteroid))
     {:noreply, moved_asteroid}
   end

   def handle_cast(:stop, b) do
     {:stop, :normal, b}
   end

   def handle_info(:tick, a) do
     GenServer.cast(self(), :move)
     Process.send_after(self(), :tick, a.tick_ms)
     {:noreply, a}
   end

   @doc """
   Split a single asteroid into two smaller asteroids heading
   in different directions and return a list of the new rocks,
   or return an empty list if the rock is too small to split.
   """
   def handle_call(:split, _game_pid, a) do
     if a.radius >= @splittable_radius_m do
       directions = [@quarter_pi_radians, -1 * @quarter_pi_radians]
       fragments = Enum.map(directions,
        fn(delta_theta) ->
          delta_theta
          |> Velocity.perturb
          |> cleave(a)
        end)
       {:reply, fragments, a}
     else
       {:reply, [], a}
     end
   end

   # {:ok, a} = Asteroid.Server.start_link(1)
   # Asteroid.Server.position(a)
   # Asteroid.Server.move(a,1)

   def random_asteroid do
     p = Elixoids.Space.random_point_on_border()
     v = Velocity.random_direction_with_speed(@asteroid_speed_m_per_s)
     %{:pos => p, :velocity => v, :radius => @asteroid_radius_m}
   end

   # Functions

   def move_asteroid(a, delta_t_ms) do
     p1 = Point.apply_velocity(a.pos, a.velocity, delta_t_ms)
     p2 = Space.wrap(p1)
     %{a | :pos => p2}
   end

   @doc """
   The tuple that will be shown to the UI for rendering.
   """
   def state_tuple(a) do
     {a.id,
      Point.round(a.pos.x),
      Point.round(a.pos.y),
      Point.round(a.radius)}
   end

   @doc """
   Remove the id of the asteroid
   """
   def anonymous(a) do
     Map.delete(a, :id)
   end

   @doc """
   Halve the radius of the asteroid.
   """
   def halve(a) do
     r = a.radius / 2.0
     %{a | radius: r}
   end

   @doc """
   Change the direction of the asteroid by given angle
   """
   def redirect(a, delta_theta) do
     update_in(a.velocity, &Velocity.fork(&1, delta_theta))
   end

   @doc """
   Double the speed of the asteroid.
   """
   def speedup(a) do
     update_in(a.velocity, &Velocity.double(&1))
   end

   @doc """
   Bump the asteroid in the direction it is facing,
   by half its radius
   """
   def bump(a) do
     t_ms = (a.radius / a.velocity.speed) * 1000 / 2
     update_in(a.pos, &Point.apply_velocity(&1, a.velocity, t_ms))
   end

   def cleave(delta_theta, a) do
     a
     |> anonymous
     |> halve
     |> redirect(delta_theta)
     |> speedup
     |> bump
   end

end

defmodule Asteroid.Server do

   @moduledoc """
   Asteroid process. Asteroids have position, size and velocity,
   and wander the game area.
   """

   use GenServer

   alias World.Clock,    as: Clock
   alias World.Point,    as: Point
   alias World.Velocity, as: Velocity
   alias Elixoids.Space, as: Space

   # Radius of random asteroid
   @asteroid_radius_m     120.0

   # Smallest asteroid that can survive being hit
   @splittable_radius_m    20.0

   # 45º
   @quarter_pi_radians (:math.pi / 4.0)

   # Initial speed of asteroid
   @asteroid_speed_m_per_s 20.0

   def start_link(id, game_pid, asteroid \\ random_asteroid()) do
     a = Map.merge(asteroid, %{
      :id=>id,
      :game_pid=>game_pid,
      :clock_ms=>Clock.now_ms,
      :tick_ms=>Clock.ms_between_frames})

     GenServer.start_link(__MODULE__, a, [])
   end

   @doc """
   Return the state of the process as a tuple
   """
   def position(pid) do
     GenServer.call(pid, :position)
   end

   @doc """
   The asteroid has been destroyed.

      {:ok, a} = Asteroid.Server.start_link(9999)
      Process.alive?(a)
      Asteroid.Server.stop(a)
      Process.alive?(a)
   """
   def stop(pid) do
     GenServer.cast(pid, :stop)
   end

   @doc """
   Return a list of zero or two new asteroid states.

   Returns empty list if the asteroid is too small to be split.
   Otherwise returns a list of two new states of smaller rocks
   flying in opposite directions.

   {:ok, game} = Game.Server.start_link(60)
   Game.Server.show(game)

   rock = IEx.Helpers.pid(0,140,0)
   Asteroid.Server.split(rock)
   """
   def split(pid) do
     GenServer.call(pid, :split)
   end

   # GenServer callbacks

   def init(a) do
     Process.send(self(), :tick, [])
     {:ok, a}
   end

   def handle_cast(:move, a) do
     delta_t_ms = Clock.since(a.clock_ms)

     moved_asteroid = a
     |> move_asteroid(delta_t_ms)
     |> Map.put(:clock_ms, Clock.now_ms)

     Game.Server.update_asteroid(a.game_pid, state_tuple(moved_asteroid))
     {:noreply, moved_asteroid}
   end

   def handle_cast(:stop, b) do
     {:stop, :normal, b}
   end

   def handle_info(:tick, a) do
     GenServer.cast(self(), :move)
     Process.send_after(self(), :tick, a.tick_ms)
     {:noreply, a}
   end

   def handle_call(:position, _from, a) do
     {:reply, {a.id, a.pos.x, a.pos.y, a.radius}, a}
   end

   @doc """
   Split a single asteroid into two smaller asteroids heading
   in different directions and return a list of the new rocks,
   or return an empty list if the rock is too small to split.
   """
   def handle_call(:split, _game_pid, a) do
     if a.radius >= @splittable_radius_m do
       directions = [@quarter_pi_radians, -1 * @quarter_pi_radians]
       fragments = Enum.map(directions,
        fn(delta_theta) ->
          delta_theta
          |> Velocity.perturb
          |> cleave(a)
        end)
       {:reply, fragments, a}
     else
       {:reply, [], a}
     end
   end

   # {:ok, a} = Asteroid.Server.start_link(1)
   # Asteroid.Server.position(a)
   # Asteroid.Server.move(a,1)

   def random_asteroid do
     p = Elixoids.Space.random_point_on_border()
     v = Velocity.random_direction_with_speed(@asteroid_speed_m_per_s)
     %{:pos => p, :velocity => v, :radius => @asteroid_radius_m}
   end

   # Functions

   def move_asteroid(a, delta_t_ms) do
     p1 = Point.apply_velocity(a.pos, a.velocity, delta_t_ms)
     p2 = Space.wrap(p1)
     %{a | :pos => p2}
   end

   @doc """
   The tuple that will be shown to the UI for rendering.
   """
   def state_tuple(a) do
     {a.id,
      Point.round(a.pos.x),
      Point.round(a.pos.y),
      Point.round(a.radius)}
   end

   @doc """
   Remove the id of the asteroid
   """
   def anonymous(a) do
     Map.delete(a, :id)
   end

   @doc """
   Halve the radius of the asteroid.
   """
   def halve(a) do
     r = a.radius / 2.0
     %{a | radius: r}
   end

   @doc """
   Change the direction of the asteroid by given angle
   """
   def redirect(a, delta_theta) do
     update_in(a.velocity, &Velocity.fork(&1, delta_theta))
   end

   @doc """
   Double the speed of the asteroid.
   """
   def speedup(a) do
     update_in(a.velocity, &Velocity.double(&1))
   end

   @doc """
   Bump the asteroid in the direction it is facing,
   by half its radius
   """
   def bump(a) do
     t_ms = (a.radius / a.velocity.speed) * 1000 / 2
     update_in(a.pos, &Point.apply_velocity(&1, a.velocity, t_ms))
   end

   def cleave(delta_theta, a) do
     a
     |> anonymous
     |> halve
     |> redirect(delta_theta)
     |> speedup
     |> bump
   end

end


Bind constraint name

Bind constraint name

defmodule Tokenizer.DB.Schemas.Payment do
  @moduledoc """
  Model for payments.
  """
  use Tokenizer.Web, :schema
  import Tokenizer.DB.Changeset.DynamicEmbeds
  import Tokenizer.DB.Changeset.Validators.Fee

  schema "payments" do
    field :external_id, :string
    field :token, :string
    field :token_expires_at, :utc_datetime
    field :amount, :decimal
    field :fee, :decimal
    field :description, :string
    field :status, Tokenizer.DB.Enums.PaymentStatuses, default: :authorization
    field :auth, Tokenizer.DB.Types.Authorization
    field :metadata, :map
    embeds_one :sender, Tokenizer.DB.Schemas.Peer
    embeds_one :recipient, Tokenizer.DB.Schemas.Peer

    timestamps()
  end

  def insert(%Ecto.Changeset{} = changeset) do
    changeset
    |> Ecto.Changeset.apply_changes()
    |> Tokenizer.DB.Repo.insert
  end

  def insert(%Tokenizer.DB.Schemas.Payment{} = struct) do
    struct
    |> Tokenizer.DB.Repo.insert
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.

  It should be used to validate data that is sent by API consumers,
  so we don't force them to send internally required fields.
  """
  def changeset(struct, params \\ %{}) do
    limits = Confex.get(:tokenizer_api, :limits)
    fees = Confex.get(:tokenizer_api, :fees)

    struct
    |> cast(params, [:amount, :fee, :description, :metadata])
    |> cast_dynamic_embed(:auth)
    |> cast_embed(:sender, with: &Tokenizer.DB.Schemas.Peer.sender_changeset/2)
    |> cast_embed(:recipient, with: &Tokenizer.DB.Schemas.Peer.recipient_changeset/2)
    |> validate_required([:amount, :fee, :description, :sender, :recipient])
    |> validate_number(:amount,
        greater_than_or_equal_to: limits[:amount][:min],
        less_than_or_equal_to: limits[:amount][:max])
    |> validate_number(:fee, greater_than: 0)
    |> validate_length(:description, max: 250)
    |> validate_fee(:amount, :fee, fees)
    |> validate_metadata(:metadata)
    |> unique_constraint(:external_id, name: :payments_external_id_index)
  end
end

defmodule Tokenizer.DB.Schemas.Payment do
  @moduledoc """
  Model for payments.
  """
  use Tokenizer.Web, :schema
  import Tokenizer.DB.Changeset.DynamicEmbeds
  import Tokenizer.DB.Changeset.Validators.Fee

  schema "payments" do
    field :external_id, :string
    field :token, :string
    field :token_expires_at, :utc_datetime
    field :amount, :decimal
    field :fee, :decimal
    field :description, :string
    field :status, Tokenizer.DB.Enums.PaymentStatuses, default: :authorization
    field :auth, Tokenizer.DB.Types.Authorization
    field :metadata, :map
    embeds_one :sender, Tokenizer.DB.Schemas.Peer
    embeds_one :recipient, Tokenizer.DB.Schemas.Peer

    timestamps()
  end

  def insert(%Ecto.Changeset{} = changeset) do
    changeset
    |> Ecto.Changeset.apply_changes()
    |> Tokenizer.DB.Repo.insert
  end

  def insert(%Tokenizer.DB.Schemas.Payment{} = struct) do
    struct
    |> Tokenizer.DB.Repo.insert
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.

  It should be used to validate data that is sent by API consumers,
  so we don't force them to send internally required fields.
  """
  def changeset(struct, params \\ %{}) do
    limits = Confex.get(:tokenizer_api, :limits)
    fees = Confex.get(:tokenizer_api, :fees)

    struct
    |> cast(params, [:amount, :fee, :description, :metadata])
    |> cast_dynamic_embed(:auth)
    |> cast_embed(:sender, with: &Tokenizer.DB.Schemas.Peer.sender_changeset/2)
    |> cast_embed(:recipient, with: &Tokenizer.DB.Schemas.Peer.recipient_changeset/2)
    |> validate_required([:amount, :fee, :description, :sender, :recipient])
    |> validate_number(:amount,
        greater_than_or_equal_to: limits[:amount][:min],
        less_than_or_equal_to: limits[:amount][:max])
    |> validate_number(:fee, greater_than: 0)
    |> validate_length(:description, max: 250)
    |> validate_fee(:amount, :fee, fees)
    |> validate_metadata(:metadata)
    |> unique_constraint(:external_id)
  end
end