discovery-tecnologia/dsc-mercado-livre

AccessToken abstrato

Closed this issue · 10 comments

Estou desenvolvendo uma integração e me deparei com um acoplamento de Storage e AccessToken, no sistema que estou desenvolvendo eu preciso separar o token dos usuários pois o sistema fara a integração de diversos usuários simultaneamente, porem a lib permite apenas o armazenamento de uma unica chave de token não permitindo que eu define qual a chave sera utilizada para armazenar.

const TOKEN = 'token';

A classe AccessToken é instanciada diretamente na classe de AuthorizationService impossibilitando de que um adapter seja utilizado.

$accessToken = new AccessToken($storage);

Existe alguma maneira de resolver essa questão ou é um ponto em que precisamos alterar a lib?
O que eu preciso é armazenar N tokens onde a chave do token será um identificador unico para cada usuario.

Eu resolvi a situação criando alguns Adapters para as classes AuthorizationService, Production e StorageInterface, funciona mas acredito que deveria ser mais simples mudar a estrategia de salvar os dados de tokens 👀

Otima observacao @rafa-acioly
Sim... realmente temos um problema ae!

Se vc quiser abrir uma PR pra gente analisar...
Valeu pela ajuda!

Cara, eh um problemao hehe

Fiz uma analise aqui, acredito que conseguimos dois caminhos
Primeiro: partir talvez para a criacao de um CustomEnvironment (nao sei ainda hehe) - acho que segue muito a linha que vc esta pensando mesmo, que eu acho legal tambem.

Segundo: talvez um pouco mais simples... seria, usar um terceiro parametro na class Meli
Pensei ate em usar o clientId pra isso, mas salvar na chave de cache/session nao acho legal...

Acho que teriamos que alterar a MeliInterface

<?php
namespace Dsc\MercadoLivre;

interface MeliInterface
{
    // outros metodos

    /**
     * @return $tenant
     */
    public function getTenant();

    /**
     * @param string $name
     */
    public function setTenant($name);
}

Alterar a classe Meli para receber o tenant como parametro

/**
 * Class Meli
 */
class Meli implements MeliInterface
{
    // outros atributos

   /**
    * Tentant name
    */
    private $tenant;

    public function __construct($clientId, $clientSecret, Environment $environment = null, $tenant = null)
    {
        $this->clientId     = $clientId;
        $this->clientSecret = $clientSecret;
        $this->environment  = $environment ?: new Production();
        $this->tenant = $tenant;
    }

    public function setTenant($name) {
         $this->tenant = $name;
    }
    
    public function getTenant() {
         return $this->tenant;
    }
   
    // outros metodos
}

A AuthorizationService busca a referencia dessa classe sempre

$meli = $this->getMeli();

Ae temos que alterar a classe AccessToken para receber o $tenant e considerar que ele sempre considera esse name com a chave do cache.

<?php
namespace Dsc\MercadoLivre;

use Dsc\MercadoLivre\Storage\SessionManager;
use Dsc\MercadoLivre\Storage\StorageInterface;

class AccessToken
{
    const TOKEN = 'token';
    const REFRESH_TOKEN = 'refresh_token';
    const EXPIRE_IN = 'expire_in';

    /**
     * @var StorageInterface
     */
    private $storage;

    private $tenant;

    public function __construct(StorageInterface $storage = null, $tenant = null)
    {
        $this->storage = $storage ? $storage : new SessionManager();
        $this->tenant   = $tenant ?: static::TOKEN;
    }

    /**
     * @return string|bool
     */
    public function getToken()
    {
        return $this->storage->get(static::TOKEN.$this->tenant);
    }

    /**
     * @param string $token
     */
    public function setToken($token)
    {
        $this->storage->set(static::TOKEN.$this->tenant, $token);
    }

    /**
     * @return string
     */
    public function getRefreshToken()
    {
        return $this->storage->get(static::REFRESH_TOKEN.$this->tenant);
    }

    /**
     * @param string $refreshToken
     */
    public function setRefreshToken($refreshToken)
    {
        $this->storage->set(static::REFRESH_TOKEN.$this->tenant, $refreshToken);
    }

    /**
     * @return int
     */
    public function getExpireIn()
    {
        return $this->storage->get(static::EXPIRE_IN.$this->tenant);
    }

    /**
     * @param int $expireIn
     */
    public function setExpireIn($expireIn)
    {
        $this->storage->set(static::EXPIRE_IN.$this->tenant, time() + $expireIn);
    }

    /**
     * @return bool
     */
    public function isExpired()
    {
        if($this->storage->has(static::EXPIRE_IN.$this->tenant) &&
            $this->storage->get(static::EXPIRE_IN.$this->tenant) >= time()) {
            return false;
        }
        return true;
    }

    /**
     * @return bool
     */
    public function isValid()
    {
        if(! $this->getToken() || $this->isExpired()) {
            return false;
        }
        return true;
    }
}

E alterar no AuthorizationService para "inserir" no AccessToken

/**
     * @param array $data
     * @return string
     * @throws MeliException
     */
    private function getToken($data)
    {
        $environment = $this->getMeli()->getEnvironment(); 
        $tenant = $this->getMeli()->getTenant(); 
        // ......
        $accessToken = new AccessToken($storage, $tenant);
    }

// ......

Quando instanciamos um novo Meli passamos o tenant

        $meli = new Meli(
            'APP-ID', 
            'SECRET-ID',
            new Production(Site::BRASIL),
            'MINHA_LOJA_ID'
        );

O que vc acha?? @rafa-acioly

Pensando bem....
Acho que vou usar a classe Environment pra isso hehe

$meli = new Meli(
   'APP-ID', 
   'SECRET-ID',
   new Production(Site::BRASIL, 'MINHA_LOJA_ID')
);

Acredito que essa opção de inserir o MINHA_LOJA_ID em Environment seja a melhor, ao que parece as alterações seriam menores

Cara, mudei de ideia novamente hehe
Acredito que nao teriamos problema setando o client-id como chave, ate porque esses dados ficam no server e criptografados.

Entao, nao muda nada na implementacao... usando a nova versao 2.1.0-rc esse suporte ja deve funcionar com as multi-contas.

@dilowagner mas o client-id é fixo certo? não teriamos como identificar qual é o tenant que esta fazendo a requisição para o mercado livre...
não entendi bem a sugestão que você diz para setar o client-id como chave, o unico valor passado para storage é a palavra token, dessa maneira não conseguimos separar diversos tokens de diversos clientes.

Opa @rafa-acioly entao....
Acredito que nao, correto?
O client-id vai mudar com cada cliente que conectar na sua plataforma/app.

Eu tenho um caso de uso parecido em um sistema que trabalhava... onde o cliente precisava configurar seus dados de acesso ao mercado livre (client-id e client-secret)
Voce nao usa o client-id diferente pra cada cliente? como vc separaria as contas? todo mundo publica com o mesmo client-id?

Isso e o que indica o cliente unico la nas aplicacoes do Mercado Livre
https://developers.mercadolivre.com.br/pt_br/registre-o-seu-aplicativo

Ha e nao comentei....
Exatamente, o que mudei foi para que a chave do storage considerasse o client-id tambem:

$key = static::TOKEN . $this->tenant

implementado em d175b7b