PROFESSIONAL NEWSGROUPSWINDEV , WEBDEV and WINDEV Mobile
Started by Boller, Apr., 03 2025 8:55 AM - 17 replies
Registered member 3,851 messages
Posted on April, 03 2025 - 8:55 AM
Versao 1.0 /////////////////////////////////////////////////////////////////////////// // Classe RESTAPIClient // Versão: 1.0 // Autor: Adriano // Descrição: Classe OOP para facilitar integração com APIs REST // Suporta múltiplos métodos de autenticação de forma padronizada /////////////////////////////////////////////////////////////////////////// CLASSE RESTAPIClient //======================================================================= // ATRIBUTOS PRIVADOS //======================================================================= PRIVATE // Configurações básicas m_URL is string // URL base da API m_Timeout is int = 30 // Timeout em segundos m_ContentType is string = "application/json" // Tipo de conteúdo padrão m_DefaultHeaders is array of string dynamic // Array para armazenar cabeçalhos padrões // Configurações de autenticação m_AuthType is int = authNone // Tipo de autenticação (constantes definidas abaixo) m_Username is string // Para Basic Auth m_Password is string // Para Basic Auth m_Token is string // Para Bearer/JWT m_APIKey is string // Para API Key m_APIKeyName is string = "X-API-Key" // Nome do cabeçalho para API Key m_APIKeyLocation is int = keyHeader // Localização da API Key (header, query, etc) m_CertificatePath is string // Para mTLS m_CertificatePassword is string // Para mTLS m_OAuthClientID is string // Para OAuth 2.0 m_OAuthClientSecret is string // Para OAuth 2.0 m_OAuthTokenURL is string // URL para obtenção de token OAuth m_HMACSecret is string // Chave secreta para HMAC // Tokens OAuth m_AccessToken is string // Token de acesso atual m_RefreshToken is string // Token de atualização m_TokenExpiry is datetime // Data/hora de expiração do token // Configurações de log e debug m_EnableLogging is boolean = False // Ativar logging de requisições m_LogPath is string // Caminho para salvar logs m_LastResponse is httpResponse // Última resposta HTTP recebida m_LastRequest is httpRequest // Última requisição HTTP enviada //======================================================================= // CONSTANTES INTERNAS //======================================================================= PUBLIC CONSTANT // Tipos de autenticação authNone = 0 // Sem autenticação authBasic = 1 // Autenticação básica (usuário/senha) authBearer = 2 // Bearer token / JWT authAPIKey = 3 // Chave de API authOAuth2 = 4 // OAuth 2.0 authHMAC = 5 // HMAC (assinatura) authMTLS = 6 // Mutual TLS (certificados) authCustom = 7 // Autenticação personalizada // Localizações para API Key keyHeader = 1 // API Key no cabeçalho keyQuery = 2 // API Key na query string // Métodos HTTP httpMethodGET = httpGet // GET - Obter dados httpMethodPOST = httpPost // POST - Enviar dados httpMethodPUT = httpPut // PUT - Atualizar dados httpMethodDELETE = httpDelete // DELETE - Remover dados httpMethodPATCH = httpPatch // PATCH - Atualização parcial //======================================================================= // MÉTODOS PÚBLICOS //======================================================================= PUBLIC //------------------------------------------------------------------- // Construtor - Inicializa a classe com a URL base //------------------------------------------------------------------- PROCEDURE Constructor(URLAPI is string) // Inicializa a URL base da API m_URL = URLAPI // Inicializa array de cabeçalhos padrão ArrayAdd(m_DefaultHeaders, "User-Agent", "WLanguage-API-Client/1.0") ArrayAdd(m_DefaultHeaders, "Accept", "application/json") // Log de inicialização IF m_EnableLogging THEN LogOperation("Inicialização do cliente API para: " + m_URL) END //------------------------------------------------------------------- // Configura timeout para requisições //------------------------------------------------------------------- PROCEDURE SetTimeout(Segundos is int) // Atualiza o timeout com validação IF Segundos > 0 THEN m_Timeout = Segundos LogOperation("Timeout configurado para: " + Segundos + " segundos") ELSE Error("Timeout deve ser maior que zero") END RESULT True END //------------------------------------------------------------------- // Configura o tipo de conteúdo padrão para requisições //------------------------------------------------------------------- PROCEDURE SetContentType(TipoConteudo is string) // Atualiza o tipo de conteúdo m_ContentType = TipoConteudo LogOperation("Tipo de conteúdo configurado para: " + TipoConteudo) RESULT True END //------------------------------------------------------------------- // Adiciona um cabeçalho padrão para todas as requisições //------------------------------------------------------------------- PROCEDURE AddDefaultHeader(Nome is string, Valor is string) // Adiciona ou atualiza o cabeçalho no array bEncontrado is boolean = False // Procura se já existe este cabeçalho FOR i = 1 TO ArrayCount(m_DefaultHeaders) STEP 2 IF m_DefaultHeaders[i] = Nome THEN // Atualiza valor existente m_DefaultHeaders[i+1] = Valor bEncontrado = True BREAK END END // Se não encontrou, adiciona novo IF bEncontrado = False THEN ArrayAdd(m_DefaultHeaders, Nome) ArrayAdd(m_DefaultHeaders, Valor) END LogOperation("Cabeçalho padrão adicionado/atualizado: " + Nome + " = " + Valor) RESULT True END //------------------------------------------------------------------- // Remove um cabeçalho padrão pelo nome //------------------------------------------------------------------- PROCEDURE RemoveDefaultHeader(Nome is string) // Procura e remove o cabeçalho FOR i = 1 TO ArrayCount(m_DefaultHeaders) STEP 2 IF m_DefaultHeaders[i] = Nome THEN // Remove nome e valor (dois elementos) ArrayDelete(m_DefaultHeaders, i) ArrayDelete(m_DefaultHeaders, i) // Agora i+1 virou i LogOperation("Cabeçalho padrão removido: " + Nome) RESULT True END END // Não encontrou LogOperation("Cabeçalho não encontrado para remover: " + Nome) RESULT False END //------------------------------------------------------------------- // Configura autenticação básica (usuário e senha) //------------------------------------------------------------------- PROCEDURE SetBasicAuth(Usuario is string, Senha is string) // Valida parâmetros IF Usuario = "" THEN Error("Usuário não pode ser vazio para autenticação básica") RESULT False END // Configura autenticação básica m_AuthType = authBasic m_Username = Usuario m_Password = Senha LogOperation("Autenticação Básica configurada para usuário: " + Usuario) RESULT True END //------------------------------------------------------------------- // Configura autenticação JWT/Bearer Token //------------------------------------------------------------------- PROCEDURE SetBearerAuth(Token is string) // Valida parâmetros IF Token = "" THEN Error("Token não pode ser vazio para autenticação Bearer") RESULT False END // Configura autenticação Bearer m_AuthType = authBearer m_Token = Token LogOperation("Autenticação Bearer Token configurada") RESULT True END //------------------------------------------------------------------- // Configura autenticação via API Key //------------------------------------------------------------------- PROCEDURE SetAPIKeyAuth(APIKey is string, NomeParametro is string = "X-API-Key", Localizacao is int = keyHeader) // Valida parâmetros IF APIKey = "" THEN Error("API Key não pode ser vazia") RESULT False END // Configura autenticação API Key m_AuthType = authAPIKey m_APIKey = APIKey m_APIKeyName = NomeParametro m_APIKeyLocation = Localizacao LogOperation("Autenticação API Key configurada: " + NomeParametro) RESULT True END //------------------------------------------------------------------- // Configura autenticação via OAuth 2.0 //------------------------------------------------------------------- PROCEDURE SetOAuth2Auth(ClientID is string, ClientSecret is string, TokenURL is string) // Valida parâmetros IF ClientID = "" OR ClientSecret = "" OR TokenURL = "" THEN Error("Parâmetros OAuth incompletos") RESULT False END // Configura autenticação OAuth 2.0 m_AuthType = authOAuth2 m_OAuthClientID = ClientID m_OAuthClientSecret = ClientSecret m_OAuthTokenURL = TokenURL LogOperation("Autenticação OAuth 2.0 configurada para: " + TokenURL) RESULT True END //------------------------------------------------------------------- // Define manualmente tokens OAuth (útil para restaurar sessão) //------------------------------------------------------------------- PROCEDURE SetOAuthTokens(AccessToken is string, RefreshToken is string, ExpirySeconds is int) // Valida parâmetros IF AccessToken = "" THEN Error("Access token não pode ser vazio") RESULT False END // Configura tokens m_AccessToken = AccessToken m_RefreshToken = RefreshToken // Calcula data de expiração IF ExpirySeconds > 0 THEN m_TokenExpiry = DateTimeAddSecond(Now(), ExpirySeconds) ELSE // Token sem expiração definida, usa padrão de 1 hora m_TokenExpiry = DateTimeAddSecond(Now(), 3600) END LogOperation("Tokens OAuth configurados manualmente") RESULT True END //------------------------------------------------------------------- // Configura autenticação HMAC (baseada em assinatura) //------------------------------------------------------------------- PROCEDURE SetHMACAuth(ChaveSecreta is string) // Valida parâmetros IF ChaveSecreta = "" THEN Error("Chave secreta HMAC não pode ser vazia") RESULT False END // Configura autenticação HMAC m_AuthType = authHMAC m_HMACSecret = ChaveSecreta LogOperation("Autenticação HMAC configurada") RESULT True END //------------------------------------------------------------------- // Configura autenticação mTLS (certificado mútuo) //------------------------------------------------------------------- PROCEDURE SetMTLSAuth(CaminhoCertificado is string, SenhaCertificado is string = "") // Valida parâmetros IF CaminhoCertificado = "" THEN Error("Caminho do certificado não pode ser vazio") RESULT False END // Verifica se o arquivo existe IF fFileExist(CaminhoCertificado) = False THEN Error("Certificado não encontrado: " + CaminhoCertificado) RESULT False END // Configura autenticação mTLS m_AuthType = authMTLS m_CertificatePath = CaminhoCertificado m_CertificatePassword = SenhaCertificado LogOperation("Autenticação mTLS configurada com certificado: " + CaminhoCertificado) RESULT True END //------------------------------------------------------------------- // Habilita ou desabilita logging das operações //------------------------------------------------------------------- PROCEDURE SetLogging(Ativar is boolean, CaminhoLogs is string = "") // Configura logging m_EnableLogging = Ativar IF Ativar AND CaminhoLogs <> "" THEN m_LogPath = CaminhoLogs // Verifica/cria diretório IF fDirectoryExist(CaminhoLogs) = False THEN fMakeDir(CaminhoLogs) END END // Log da operação IF Ativar THEN LogOperation("Logging ativado em: " + (m_LogPath <> "" ? m_LogPath : "saída padrão")) ELSE LogOperation("Logging desativado") END RESULT True END //------------------------------------------------------------------- // Executa requisição GET //------------------------------------------------------------------- PROCEDURE Get(Endpoint is string, Parametros is string = "", Cabecalhos is array of string dynamic = Null) // Executa uma requisição GET RESULT ExecuteRequest(httpMethodGET, Endpoint, Parametros, "", Cabecalhos) END //------------------------------------------------------------------- // Executa requisição POST //------------------------------------------------------------------- PROCEDURE Post(Endpoint is string, Dados is variant, Cabecalhos is array of string dynamic = Null) // Executa uma requisição POST DadosString is string = ConvertData(Dados) RESULT ExecuteRequest(httpMethodPOST, Endpoint, "", DadosString, Cabecalhos) END //------------------------------------------------------------------- // Executa requisição PUT //------------------------------------------------------------------- PROCEDURE Put(Endpoint is string, Dados is variant, Cabecalhos is array of string dynamic = Null) // Executa uma requisição PUT DadosString is string = ConvertData(Dados) RESULT ExecuteRequest(httpMethodPUT, Endpoint, "", DadosString, Cabecalhos) END //------------------------------------------------------------------- // Executa requisição DELETE //------------------------------------------------------------------- PROCEDURE Delete(Endpoint is string, Parametros is string = "", Cabecalhos is array of string dynamic = Null) // Executa uma requisição DELETE RESULT ExecuteRequest(httpMethodDELETE, Endpoint, Parametros, "", Cabecalhos) END //------------------------------------------------------------------- // Executa requisição PATCH //------------------------------------------------------------------- PROCEDURE Patch(Endpoint is string, Dados is variant, Cabecalhos is array of string dynamic = Null) // Executa uma requisição PATCH DadosString is string = ConvertData(Dados) RESULT ExecuteRequest(httpMethodPATCH, Endpoint, "", DadosString, Cabecalhos) END //------------------------------------------------------------------- // Obtém a última resposta como JSON //------------------------------------------------------------------- PROCEDURE GetResponseJSON() // Verifica se existe resposta IF m_LastResponse = Null THEN Error("Nenhuma resposta disponível para conversão") RESULT Null END // Tenta converter para JSON TRY JsonData is JSON = JSONToValue(m_LastResponse.Content) RESULT JsonData CATCH Error("Falha ao converter resposta para JSON: " + ErrorInfo()) RESULT Null END END //------------------------------------------------------------------- // Obtém código de status da última resposta //------------------------------------------------------------------- PROCEDURE GetStatusCode() // Retorna código de status da última resposta IF m_LastResponse <> Null THEN RESULT m_LastResponse.StatusCode ELSE RESULT 0 END END //------------------------------------------------------------------- // Verifica se a última requisição foi bem-sucedida (2xx) //------------------------------------------------------------------- PROCEDURE IsSuccess() // Verifica se o status está na faixa de sucesso (2xx) StatusCode is int = GetStatusCode() RESULT (StatusCode >= 200 AND StatusCode < 300) END //------------------------------------------------------------------- // Obtém mensagem de erro em caso de falha //------------------------------------------------------------------- PROCEDURE GetErrorMessage() // Se não houve resposta, retorna erro genérico IF m_LastResponse = Null THEN RESULT "Nenhuma resposta recebida" END // Verifica se foi sucesso IF IsSuccess() THEN RESULT "" END // Tenta extrair mensagem de erro do corpo JSON TRY JsonData is JSON = JSONToValue(m_LastResponse.Content) // Verifica campos comuns de erro em APIs IF JsonData.error <> Null THEN IF JsonData.error.message <> Null THEN RESULT JsonData.error.message ELSE RESULT JsonData.error END ELSEIF JsonData.message <> Null THEN RESULT JsonData.message ELSEIF JsonData.errorMessage <> Null THEN RESULT JsonData.errorMessage ELSEIF JsonData.errors <> Null AND JsonData.errors..Count > 0 THEN // Tenta pegar o primeiro erro de um array de erros RESULT JsonData.errors[1] ELSE // Retorna código e descrição HTTP padrão RESULT "Erro HTTP " + m_LastResponse.StatusCode + ": " + m_LastResponse.StatusInfo END CATCH // Não conseguiu extrair erro do JSON, retorna descrição HTTP RESULT "Erro HTTP " + m_LastResponse.StatusCode + ": " + m_LastResponse.StatusInfo END END //======================================================================= // MÉTODOS PRIVADOS //======================================================================= PRIVATE //------------------------------------------------------------------- // Registra operações em log se ativado //------------------------------------------------------------------- PROCEDURE LogOperation(Mensagem is string) // Verifica se logging está ativado IF m_EnableLogging = False THEN RETURN // Formata mensagem com timestamp MensagemLog is string = DateToString(Today(), "yyyy-MM-dd") + " " + TimeToString(Now(), "HH:mm:ss") + " - " + Mensagem // Decide onde salvar o log IF m_LogPath <> "" THEN // Formato do nome do arquivo: api_log_YYYY-MM-DD.log NomeArquivo is string = m_LogPath + [fSep] + "api_log_" + DateToString(Today(), "yyyy-MM-dd") + ".log" fSaveText(NomeArquivo, MensagemLog + CR, fAddText) ELSE // Log para console/trace Trace(MensagemLog) END END //------------------------------------------------------------------- // Converte dados para formato string adequado //------------------------------------------------------------------- PROCEDURE ConvertData(Dados is variant) // Se já é string, retorna direto IF TypeInfo(Dados, tiIsString) THEN RESULT Dados END // Se é JSON, converte para string IF TypeInfo(Dados, tiIsJSON) THEN RESULT ValueToJSON(Dados) END // Array ou objeto, tenta converter para JSON IF TypeInfo(Dados, tiIsArray) OR TypeInfo(Dados, tiIsObject) THEN TRY RESULT ValueToJSON(Dados) CATCH Error("Não foi possível converter dados para JSON: " + ErrorInfo()) RESULT "" END END // Para outros tipos, tenta conversão para string TRY RESULT Dados CATCH Error("Tipo de dados não suportado para envio") RESULT "" END END //------------------------------------------------------------------- // Constrói a URL completa da requisição //------------------------------------------------------------------- PROCEDURE BuildURL(Endpoint is string, Parametros is string = "") // Remove barra à direita da URL base e à esquerda do endpoint se existirem BaseURL is string = m_URL IF Right(BaseURL, 1) = "/" THEN BaseURL = Left(BaseURL, Length(BaseURL) - 1) // Adiciona barra ao início do endpoint se não existir EndpointFormatado is string = Endpoint IF Left(EndpointFormatado, 1) <> "/" THEN EndpointFormatado = "/" + EndpointFormatado // Combina URL base com endpoint URLFinal is string = BaseURL + EndpointFormatado // Adiciona parâmetros de query string se existirem IF Parametros <> "" THEN // Adiciona ? se não existir na URL IF Position(URLFinal, "?") = 0 THEN URLFinal += "?" ELSE // Se já tem parâmetros, adiciona & IF Right(URLFinal, 1) <> "?" AND Right(URLFinal, 1) <> "&" THEN URLFinal += "&" END END // Adiciona parâmetros URLFinal += Parametros END // Caso tenha API Key na query string IF m_AuthType = authAPIKey AND m_APIKeyLocation = keyQuery THEN // Adiciona ? ou & se necessário IF Position(URLFinal, "?") = 0 THEN URLFinal += "?" ELSE // Se já tem parâmetros, adiciona & IF Right(URLFinal, 1) <> "?" AND Right(URLFinal, 1) <> "&" THEN URLFinal += "&" END END // Adiciona API Key à query string URLFinal += m_APIKeyName + "=" + URLEncode(m_APIKey) END RESULT URLFinal END //------------------------------------------------------------------- // Configura autenticação para a requisição //------------------------------------------------------------------- PROCEDURE ApplyAuthentication(Requisicao is httpRequest) // Configura autenticação conforme o tipo SWITCH m_AuthType CASE authNone: // Sem autenticação, não faz nada BREAK CASE authBasic: // Autenticação Básica (usuário/senha) Requisicao.AuthenticationBasic(m_Username, m_Password) BREAK CASE authBearer: // Autenticação Bearer/JWT Token Requisicao.AddHeader("Authorization", "Bearer " + m_Token) BREAK CASE authAPIKey: // API Key - apenas no cabeçalho, query string tratada em BuildURL IF m_APIKeyLocation = keyHeader THEN Requisicao.AddHeader(m_APIKeyName, m_APIKey) END BREAK CASE authOAuth2: // OAuth 2.0 // Verifica se precisa obter/renovar token IF m_AccessToken = "" OR m_TokenExpiry < Now() THEN IF RefreshOAuthToken() = False THEN Error("Falha ao obter/renovar token OAuth") BREAK END END // Adiciona token obtido Requisicao.AddHeader("Authorization", "Bearer " + m_AccessToken) BREAK CASE authHMAC: // Autenticação HMAC com assinatura // Gera timestamp para evitar replay attacks Timestamp is string = DateTimeToString(Now(), "yyyyMMddHHmmss") // Dados para assinatura: timestamp + URL + dados (se houver) DadosParaAssinar is string = Timestamp + Requisicao.URL + Requisicao.Data // Calcula assinatura HMAC SHA-256 Assinatura is string = HMACSHA256(DadosParaAssinar, m_HMACSecret) AssinaturaBase64 is string = Encode(Assinatura, encodeBASE64) // Adiciona cabeçalhos de autenticação Requisicao.AddHeader("X-API-Timestamp", Timestamp) Requisicao.AddHeader("X-API-Signature", AssinaturaBase64) BREAK CASE authMTLS: // Autenticação mTLS (certificado mútuo) Requisicao.Certificate = m_CertificatePath Requisicao.CertificatePassword = m_CertificatePassword BREAK CASE authCustom: // Autenticação personalizada - implementada por subclasses ApplyCustomAuthentication(Requisicao) BREAK END END //------------------------------------------------------------------- // Método a ser sobrescrito em subclasses para autenticação personalizada //------------------------------------------------------------------- PROCEDURE ApplyCustomAuthentication(Requisicao is httpRequest) // Método vazio para ser sobrescrito em classes derivadas // Implementação padrão não faz nada END //------------------------------------------------------------------- // Atualiza token OAuth 2.0 usando refresh token ou credentials //------------------------------------------------------------------- PROCEDURE RefreshOAuthToken() // Se temos um refresh token válido, tenta usá-lo primeiro IF m_RefreshToken <> "" THEN IF RefreshOAuthTokenWithRefreshToken() THEN RESULT True END END // Se não tem refresh token ou falhou, faz autenticação completa RESULT RefreshOAuthTokenWithCredentials() END //------------------------------------------------------------------- // Atualiza token OAuth usando refresh token //------------------------------------------------------------------- PROCEDURE RefreshOAuthTokenWithRefreshToken() // Cria requisição para o endpoint de token TokenRequest is httpRequest TokenRequest.URL = m_OAuthTokenURL TokenRequest.Method = httpPost TokenRequest.ContentType = "application/x-www-form-urlencoded" // Prepara dados para refresh token TokenRequest.Data = [ grant_type=refresh_token& refresh_token=[%m_RefreshToken%]& client_id=[%m_OAuthClientID%]& client_secret=[%m_OAuthClientSecret%] ] // Define timeout TokenRequest.Timeout = m_Timeout // Executa requisição LogOperation("Renovando token OAuth com refresh token") TokenResponse is httpResponse = HTTPSend(TokenRequest) // Verifica resultado IF TokenResponse.StatusCode = 200 THEN // Processa resposta TRY TokenData is JSON = JSONToValue(TokenResponse.Content) // Extrai tokens m_AccessToken = TokenData.access_token // Atualiza refresh token se fornecido IF TokenData.refresh_token <> Null THEN m_RefreshToken = TokenData.refresh_token END // Calcula expiração ValidadeSegundos is int = TokenData.expires_in ?? 3600 // Padrão 1h m_TokenExpiry = DateTimeAddSecond(Now(), ValidadeSegundos) LogOperation("Token OAuth renovado com sucesso") RESULT True CATCH Error("Erro ao processar resposta do token: " + ErrorInfo()) RESULT False END ELSE // Falha ao obter token LogOperation("Falha ao renovar token OAuth: " + TokenResponse.StatusCode) RESULT False END END //------------------------------------------------------------------- // Obtém token OAuth usando credenciais //------------------------------------------------------------------- PROCEDURE RefreshOAuthTokenWithCredentials() // Cria requisição para o endpoint de token TokenRequest is httpRequest TokenRequest.URL = m_OAuthTokenURL TokenRequest.Method = httpPost TokenRequest.ContentType = "application/x-www-form-urlencoded" // Prepara dados para autenticação de cliente TokenRequest.Data = [ grant_type=client_credentials& client_id=[%m_OAuthClientID%]& client_secret=[%m_OAuthClientSecret%] ] // Define timeout TokenRequest.Timeout = m_Timeout // Executa requisição LogOperation("Obtendo token OAuth com credenciais") TokenResponse is httpResponse = HTTPSend(TokenRequest) // Verifica resultado IF TokenResponse.StatusCode = 200 THEN // Processa resposta TRY TokenData is JSON = JSONToValue(TokenResponse.Content) // Extrai tokens m_AccessToken = TokenData.access_token // Extrai refresh token se disponível IF TokenData.refresh_token <> Null THEN m_RefreshToken = TokenData.refresh_token END // Calcula expiração ValidadeSegundos is int = TokenData.expires_in ?? 3600 // Padrão 1h m_TokenExpiry = DateTimeAddSecond(Now(), ValidadeSegundos) LogOperation("Token OAuth obtido com sucesso") RESULT True CATCH Error("Erro ao processar resposta do token: " + ErrorInfo()) RESULT False END ELSE // Falha ao obter token LogOperation("Falha ao obter token OAuth: " + TokenResponse.StatusCode) RESULT False END END //------------------------------------------------------------------- // Aplica cabeçalhos padrão à requisição //------------------------------------------------------------------- PROCEDURE ApplyDefaultHeaders(Requisicao is httpRequest) // Adiciona todos os cabeçalhos padrão FOR i = 1 TO ArrayCount(m_DefaultHeaders) STEP 2 Requisicao.AddHeader(m_DefaultHeaders[i], m_DefaultHeaders[i+1]) END // Define o tipo de conteúdo se não for vazio IF m_ContentType <> "" THEN Requisicao.ContentType = m_ContentType END END //------------------------------------------------------------------- // Método principal para executar qualquer tipo de requisição //------------------------------------------------------------------- PROCEDURE ExecuteRequest(Metodo is int, Endpoint is string, Parametros is string = "", Dados is string = "", Cabecalhos is array of string dynamic = Null) // Cria objeto de requisição Requisicao is httpRequest // Configura URL Requisicao.URL = BuildURL(Endpoint, Parametros) // Configura método HTTP Requisicao.Method = Metodo // Configura timeout Requisicao.Timeout = m_Timeout // Define dados do corpo se existirem IF Dados <> "" THEN Requisicao.Data = Dados END // Aplica cabeçalhos padrão ApplyDefaultHeaders(Requisicao) // Aplica cabeçalhos adicionais específicos desta requisição IF Cabecalhos <> Null THEN FOR i = 1 TO ArrayCount(Cabecalhos) STEP 2 Requisicao.AddHeader(Cabecalhos[i], Cabecalhos[i+1]) END END // Aplica autenticação ApplyAuthentication(Requisicao) // Armazena última requisição para debug m_LastRequest = Requisicao // Log da operação LogOperation("Executando requisição " + EnumToString(Metodo) + " para: " + Requisicao.URL) // Executa requisição com tratamento de erros TRY // Executa a requisição HTTP Resposta is httpResponse = HTTPSend(Requisicao) // Armazena última resposta m_LastResponse = Resposta // Log do resultado LogOperation("Resposta recebida: " + Resposta.StatusCode + " " + Resposta.StatusInfo) // Retorna resposta completa RESULT Resposta CATCH // Log do erro MessagemErro is string = "Erro ao executar requisição: " + ErrorInfo() LogOperation(MessagemErro) Error(MessagemErro) // Cria resposta vazia para evitar null pointer RespostaVazia is httpResponse RespostaVazia.StatusCode = 0 RespostaVazia.StatusInfo = "Erro de conexão: " + ErrorInfo() // Armazena como última resposta m_LastResponse = RespostaVazia // Retorna resposta vazia RESULT RespostaVazia END END END//Classe //======================================================================= // CLASSES DERIVADAS PARA CASOS DE USO ESPECÍFICOS //======================================================================= //------------------------------------------------------------------- // Classe especializada para APIs RESTful genéricas - Implementa paginação //------------------------------------------------------------------- CLASSE RESTGenericAPI HÉRITA DE RESTAPIClient // Propriedades de paginação PRIVATE m_PaginationStrategy is int = pagNone // Estratégia de paginação m_PageParam is string = "page" // Nome do parâmetro de página m_LimitParam is string = "limit" // Nome do parâmetro de limite m_DefaultLimit is int = 50 // Limite padrão de itens por página m_CurrentPage is int = 1 // Página atual PUBLIC CONSTANT // Estratégias de paginação pagNone = 0 // Sem paginação pagOffset = 1 // Paginação baseada em offset pagPage = 2 // Paginação baseada em página pagCursor = 3 // Paginação baseada em cursor PUBLIC //------------------------------------------------------------------- // Construtor //------------------------------------------------------------------- PROCEDURE Constructor(URLAPI is string) // Chama construtor da classe pai Constructor RESTAPIClient(URLAPI) END //------------------------------------------------------------------- // Configura estratégia de paginação //------------------------------------------------------------------- PROCEDURE SetPagination(Estrategia is int, ParamPagina is string = "", ParamLimite is string = "", LimitePadrao is int = 0) // Configura estratégia de paginação m_PaginationStrategy = Estrategia // Atualiza parâmetros se fornecidos IF ParamPagina <> "" THEN m_PageParam = ParamPagina IF ParamLimite <> "" THEN m_LimitParam = ParamLimite IF LimitePadrao > 0 THEN m_DefaultLimit = LimitePadrao // Reset de página atual m_CurrentPage = 1 LogOperation("Estratégia de paginação configurada: " + EnumToString(Estrategia)) RESULT True END //------------------------------------------------------------------- // Obtém próxima página de dados paginados //------------------------------------------------------------------- PROCEDURE GetNextPage(Endpoint is string) Parametros is string = "" // Aplica parâmetros de paginação conforme a estratégia SWITCH m_PaginationStrategy CASE pagOffset: // Paginação baseada em offset Offset is int = (m_CurrentPage - 1) * m_DefaultLimit Parametros = "offset=" + Offset + "&limit=" + m_DefaultLimit BREAK CASE pagPage: // Paginação baseada em página Parametros = m_PageParam + "=" + m_CurrentPage + "&" + m_LimitParam + "=" + m_DefaultLimit BREAK CASE pagCursor: // Para cursor, precisamos extrair o cursor da resposta anterior // Este é um exemplo básico, a implementação real depende da API IF m_LastResponse <> Null THEN TRY JsonData is JSON = JSONToValue(m_LastResponse.Content) IF JsonData.next_cursor <> Null THEN Parametros = "cursor=" + JsonData.next_cursor ELSE // Sem próxima página Error("Não há mais páginas para carregar") RESULT Null END CATCH // Erro ao processar cursor Error("Erro ao extrair cursor de paginação: " + ErrorInfo()) RESULT Null END ELSE // Primeira página Parametros = "cursor=" END BREAK END // Incrementa página atual (exceto para cursor) IF m_PaginationStrategy <> pagCursor THEN m_CurrentPage++ END // Executa a requisição GET com parâmetros de paginação Resposta is httpResponse = Get(Endpoint, Parametros) // Verifica se há mais páginas para cursor IF m_PaginationStrategy = pagCursor AND Resposta.StatusCode = 200 THEN TRY JsonData is JSON = JSONToValue(Resposta.Content) // Se não há próximo cursor, marca como última página IF JsonData.next_cursor = Null OR JsonData.next_cursor = "" THEN LogOperation("Última página atingida (cursor)") END CATCH // Ignorar erros de processamento aqui END END // Retorna resposta RESULT Resposta END //------------------------------------------------------------------- // Reinicia paginação //------------------------------------------------------------------- PROCEDURE ResetPagination() // Reinicia para a primeira página m_CurrentPage = 1 LogOperation("Paginação reiniciada") RESULT True END //------------------------------------------------------------------- // Obtém todos os resultados paginados (cuidado com APIs grandes) //------------------------------------------------------------------- PROCEDURE GetAllPages(Endpoint is string, LimiteMaxPaginas is int = 10) // Array para armazenar todos os dados TodosDados is array dynamic // Reinicia paginação ResetPagination() // Contador de páginas PaginasCarregadas is int = 0 // Loop para obter todas as páginas até o limite WHILE PaginasCarregadas < LimiteMaxPaginas // Incrementa contador PaginasCarregadas++ // Obtém próxima página Resposta is httpResponse = GetNextPage(Endpoint) // Verifica se sucesso IF Resposta = Null OR Resposta.StatusCode <> 200 THEN // Erro ou fim da paginação BREAK END // Processa dados TRY // Converte para JSON DadosPagina is JSON = JSONToValue(Resposta.Content) // Extrai array de itens - adaptar conforme estrutura da API DadosItens is array dynamic // Tenta vários formatos comuns de resposta IF DadosPagina.items <> Null THEN DadosItens = DadosPagina.items ELSEIF DadosPagina.data <> Null THEN DadosItens = DadosPagina.data ELSEIF DadosPagina.results <> Null THEN DadosItens = DadosPagina.results ELSEIF DadosPagina..Type = jsonArray THEN // A própria resposta é um array DadosItens = DadosPagina END // Adiciona itens ao array principal FOR EACH Item OF DadosItens ArrayAdd(TodosDados, Item) END // Verifica se há mais páginas (para estratégias offset/page) IF m_PaginationStrategy <> pagCursor THEN // Verifica se recebemos menos itens que o limite IF DadosItens..Count < m_DefaultLimit THEN LogOperation("Última página atingida (contagem < limite)") BREAK END ELSEIF m_PaginationStrategy = pagCursor THEN // Para cursor, verifica next_cursor IF DadosPagina.next_cursor = Null OR DadosPagina.next_cursor = "" THEN LogOperation("Última página atingida (sem próximo cursor)") BREAK END END CATCH Error("Erro ao processar dados da página: " + ErrorInfo()) BREAK END END LogOperation("Total de páginas carregadas: " + PaginasCarregadas) LogOperation("Total de itens recuperados: " + TodosDados..Count) // Retorna todos os dados RESULT TodosDados END END//Classe //------------------------------------------------------------------- // Classe especializada para API com autenticação personalizada //------------------------------------------------------------------- CLASSE CustomAuthAPI HÉRITA DE RESTAPIClient // Campos adicionais para autenticação personalizada PRIVATE m_AppId is string // ID da aplicação m_ApiVersion is string = "v1" // Versão da API m_DeviceId is string // ID do dispositivo m_CustomTokens is associative array of strings // Tokens personalizados PUBLIC //------------------------------------------------------------------- // Construtor //------------------------------------------------------------------- PROCEDURE Constructor(URLAPI is string, AppId is string, DeviceId is string = "") // Chama construtor da classe pai Constructor RESTAPIClient(URLAPI) // Inicializa campos m_AppId = AppId m_DeviceId = DeviceId IF m_DeviceId = "" THEN m_DeviceId = GetUUID() // Gera ID de dispositivo aleatório END // Ativa autenticação personalizada m_AuthType = authCustom END //------------------------------------------------------------------- // Define versão da API //------------------------------------------------------------------- PROCEDURE SetApiVersion(Versao is string) m_ApiVersion = Versao RESULT True END //------------------------------------------------------------------- // Adiciona token personalizado //------------------------------------------------------------------- PROCEDURE AddCustomToken(Nome is string, Valor is string) m_CustomTokens[Nome] = Valor RESULT True END //------------------------------------------------------------------- // Remove token personalizado //------------------------------------------------------------------- PROCEDURE RemoveCustomToken(Nome is string) IF m_CustomTokens[Nome] <> Null THEN ArrayDelete(m_CustomTokens, Nome) RESULT True END RESULT False END PROTECTED //------------------------------------------------------------------- // Sobrescreve método de autenticação personalizada //------------------------------------------------------------------- PROCEDURE ApplyCustomAuthentication(Requisicao is httpRequest) // Adiciona cabeçalhos padrão de autenticação personalizada Requisicao.AddHeader("X-App-ID", m_AppId) Requisicao.AddHeader("X-API-Version", m_ApiVersion) Requisicao.AddHeader("X-Device-ID", m_DeviceId) // Gera timestamp atual Timestamp is string = DateTimeToString(Now(), "yyyyMMddHHmmss") Requisicao.AddHeader("X-Timestamp", Timestamp) // Adiciona todos os tokens personalizados FOR EACH Token, Nome IN m_CustomTokens Requisicao.AddHeader("X-" + Nome, Token) END // Gera assinatura de segurança combinando dados IF m_CustomTokens["Secret"] <> Null THEN // Dados para assinatura: AppId + DeviceId + Timestamp + URL DadosParaAssinar is string = m_AppId + "|" + m_DeviceId + "|" + Timestamp + "|" + Requisicao.URL // Gera hash SHA-256 Assinatura is string = SHA256(DadosParaAssinar) // Adiciona assinatura como cabeçalho Requisicao.AddHeader("X-Signature", Assinatura) END END END//Classe //------------------------------------------------------------------- // Exemplo de uso da classe //------------------------------------------------------------------- PROCEDURE ExemploUsoAPIClient() // Exemplo 1: Uso básico com autenticação Bearer Token API is RESTAPIClient("https://api.exemplo.com ") API.SetBearerAuth("seu_token_jwt_aqui") API.SetTimeout(60) // 60 segundos // Fazer uma requisição GET Resposta is httpResponse = API.Get("/users/profile") // Verificar resultado IF API.IsSuccess() THEN // Processo dados JSON DadosJSON is JSON = API.GetResponseJSON() Info("Nome do usuário: " + DadosJSON.user.name) ELSE Error("Erro: " + API.GetErrorMessage()) END // Exemplo 2: Uso com paginação APIPaginada is RESTGenericAPI("https://api.exemplo.com ") APIPaginada.SetAPIKeyAuth("sua_api_key_aqui") APIPaginada.SetPagination(APIPaginada.pagPage, "page", "size", 25) // Obter todos os usuários (com limite de 5 páginas) TodosUsuarios is array dynamic = APIPaginada.GetAllPages("/users", 5) // Exemplo 3: Uso com autenticação personalizada APIPersonalizada is CustomAuthAPI("https://api.personalizada.com ", "APP123456") APIPersonalizada.AddCustomToken("Secret", "chave_secreta_aqui") // Fazer requisição com autenticação personalizada RespostaPersonalizada is httpResponse = APIPersonalizada.Get("/dados/exclusivos") RESULT True END -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 03 2025 - 9:32 AM
// ----------------------------------------------------------- // Exemplos de Uso da CLAPI_REST v4.0 // ----------------------------------------------------------- // Demonstração de como utilizar a classe com diversos métodos // de autenticação e cenários comuns de uso // ----------------------------------------------------------- // Autor: Claude [Baseado no trabalho original do fórum PCSoft] // Data: 03/04/2025 // ----------------------------------------------------------- // ==================== EXEMPLO 1: AUTENTICAÇÃO BÁSICA ==================== PROCÉDURE ExemploBasicAuth() // Criando a instância da classe API REST oAPI est un CLAPI_REST("https://api.exemplo.com/users ") // Configurando autenticação básica (username/senha) oAPI.SetBasicAuth("usuario_exemplo", "senha_segura") // Habilitando log para debug oAPI.EnableLogging(Vrai, "basic_auth_log.txt", 4) // Realizando uma requisição GET SI oAPI.GET() ALORS // Verificando se a requisição foi bem-sucedida SI oAPI.IsSuccess() ALORS Info("Requisição bem-sucedida! Status: " + oAPI.GetStatusCode()) // Processando resposta JSON vResposta est un variant = oAPI.ParseJSONResponse() SI vResposta <> Null ALORS // Exemplo de processamento da resposta POUR TOUT usuario DE vResposta.users Trace("Usuário encontrado: " + usuario.name + " (ID: " + usuario.id + ")") FIN FIN SINON Erreur("Erro na requisição: " + oAPI.GetStatusCode() + " - " + oAPI.GetResponseBody()) FIN SINON Erreur("Falha ao realizar a requisição") FIN // Exemplo de uso com POST stNovoUsuario est une Structure nom est une chaîne = "João Silva" email est une chaîne = "joao.silva@exemplo.com" role est une chaîne = "usuario" FIN // Nova requisição para criar um usuário oAPI.SetURL("https://api.exemplo.com/users/create ") SI oAPI.POST(, stNovoUsuario) ALORS SI oAPI.IsSuccess() ALORS vResposta = oAPI.ParseJSONResponse() Info("Usuário criado com sucesso! ID: " + vResposta.id) SINON Erreur("Erro ao criar usuário: " + oAPI.GetStatusCode()) FIN FIN FIN // ==================== EXEMPLO 2: TOKEN BEARER ==================== PROCÉDURE ExemploBearerAuth() // Criando a instância da classe API REST oAPI est un CLAPI_REST("https://api.exemplo.com/recursos ") // Configurando autenticação Bearer Token oAPI.SetBearerAuth("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c") // Configurações adicionais oAPI.SetTimeout(30) // 30 segundos oAPI.SetVerifySSL(Vrai) // Adicionando cabeçalhos personalizados oAPI.AddHeader("X-Custom-Header", "CustomValue") // Adicionando parâmetros de consulta oAPI.AddQueryParam("limit", "50") oAPI.AddQueryParam("offset", "0") // Realizando requisição GET com todos os parâmetros configurados SI oAPI.GET() ALORS SI oAPI.IsSuccess() ALORS vResposta est un variant = oAPI.ParseJSONResponse() Info("Total de recursos: " + vResposta.total) // Iterando sobre os resultados POUR TOUT recurso DE vResposta.data Trace("Recurso: " + recurso.nome + " - " + recurso.descricao) FIN SINON Erreur("Falha na requisição: " + oAPI.GetStatusCode()) FIN FIN // Exemplo de atualização de recurso com PUT stAtualizarRecurso est une Structure id est un entier = 123 nome est une chaîne = "Recurso Atualizado" descricao est une chaîne = "Nova descrição do recurso" ativo est un booléen = Vrai FIN oAPI.SetURL("https://api.exemplo.com/recursos/123 ") SI oAPI.PUT(, stAtualizarRecurso) ALORS SI oAPI.IsSuccess() ALORS Info("Recurso atualizado com sucesso!") SINON Erreur("Erro ao atualizar recurso: " + oAPI.GetResponseBody()) FIN FIN FIN // ==================== EXEMPLO 3: CHAVE DE API ==================== PROCÉDURE ExemploAPIKey() // Criando instância oAPI est un CLAPI_REST("https://api.weatherservice.com/forecast ") // Configurando autenticação com API Key oAPI.SetAPIKeyAuth("a1b2c3d4e5f6g7h8i9j0", "X-API-Key") // Adicionando parâmetros de consulta oAPI.AddQueryParam("city", "Paris") oAPI.AddQueryParam("days", "5") // Configurando cache para economizar requisições oAPI.EnableCache(Vrai, 1800) // Cache de 30 minutos // Realizando a consulta SI oAPI.GET() ALORS SI oAPI.IsSuccess() ALORS vClima est un variant = oAPI.ParseJSONResponse() Info("Previsão para: " + vClima.city) Info("Temperatura atual: " + vClima.current.temperature + "°C") POUR TOUT dia DE vClima.forecast Trace("Dia: " + dia.date + " - Min: " + dia.min + "°C, Max: " + dia.max + "°C") FIN SINON Erreur("Erro ao obter previsão: " + oAPI.GetStatusCode()) FIN FIN FIN // ==================== EXEMPLO 4: AUTENTICAÇÃO JWT ==================== PROCÉDURE ExemploJWTAuth() // Criando instância oAPI est un CLAPI_REST("https://api.secure-service.com/dashboard ") // Configurando autenticação JWT sJWTToken est une chaîne = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKwdw5eR0iSnWYykOu" oAPI.SetJWTAuth(sJWTToken) // Configurações de timeout e retry oAPI.SetTimeout(45) oAPI.SetRetryPolicy(5, 1500) // 5 tentativas, 1.5 segundos entre cada // Configurações de logging oAPI.EnableLogging(Vrai, "jwt_api_log.txt", 3) // Realizando requisição SI oAPI.GET() ALORS SI oAPI.IsSuccess() ALORS vDashboard est un variant = oAPI.ParseJSONResponse() Info("Dashboard carregado para: " + vDashboard.user.name) Info("Nível de acesso: " + vDashboard.accessLevel) // Processando estatísticas POUR TOUT metrica DE vDashboard.metrics Trace(metrica.name + ": " + metrica.value + " " + metrica.unit) FIN SINON Erreur("Erro ao acessar dashboard: " + oAPI.GetStatusCode()) // Verificando se o token expirou SI oAPI.GetStatusCode() = 401 ALORS Trace("Token JWT expirado ou inválido") FIN FIN FIN FIN // ==================== EXEMPLO 5: CERTIFICADO SSL ==================== PROCÉDURE ExemploCertificado() // Criando instância oAPI est un CLAPI_REST("https://api.banco-seguro.com/transacoes ") // Configurando autenticação com certificado sCaminhoCertificado est une chaîne = fRepExe() + [fsep] + "certificados" + [fsep] + "certificado_cliente.p12" sSenhaCertificado est une chaîne = "senhadocertificado" oAPI.SetCertificateAuth(sCaminhoCertificado, sSenhaCertificado) // Configurando dados da transação stTransacao est une Structure conta_origem est une chaîne = "12345-6" conta_destino est une chaîne = "65432-1" valor est un monétaire = 1250.75 descricao est une chaîne = "Pagamento de serviço" FIN // Enviando a transação SI oAPI.POST(, stTransacao) ALORS SI oAPI.IsSuccess() ALORS vResposta est un variant = oAPI.ParseJSONResponse() Info("Transação realizada com sucesso!") Info("Protocolo: " + vResposta.protocolo) Info("Data/Hora: " + vResposta.timestamp) SINON Erreur("Erro na transação: " + oAPI.GetStatusCode()) Erreur("Mensagem: " + oAPI.GetResponseBody()) FIN SINON Erreur("Falha ao comunicar com o servidor do banco") FIN FIN // ==================== EXEMPLO 6: OAUTH 2.0 CLIENT CREDENTIALS ==================== PROCÉDURE ExemploOAuth2() // Configurações do OAuth2 sTokenEndpoint est une chaîne = "https://auth.servico-api.com/oauth/token " sClientID est une chaîne = "app_client_id_123456" sClientSecret est une chaîne = "app_client_secret_abcdef" sScope est une chaîne = "read write" // Criando instância oAPI est un CLAPI_REST("https://api.servico-api.com/v1/products ") // Configurando autenticação OAuth2 oAPI.SetOAuth2ClientCredentials(sTokenEndpoint, sClientID, sClientSecret, sScope) // Configurações adicionais oAPI.EnableLogging(Vrai, "oauth2_log.txt", 3) oAPI.SetTimeout(60) // A classe gerenciará automaticamente a obtenção do token OAuth2 SI oAPI.GET() ALORS SI oAPI.IsSuccess() ALORS vProdutos est un variant = oAPI.ParseJSONResponse() Info("Total de produtos: " + vProdutos.total) POUR TOUT prod DE vProdutos.items Trace("Produto: " + prod.name + " - Preço: " + prod.price) FIN SINON Erreur("Erro ao obter produtos: " + oAPI.GetStatusCode()) FIN FIN // Criando um novo produto stNovoProduto est une Structure name est une chaîne = "Produto Premium" description est une chaîne = "Produto de alta qualidade" price est un monétaire = 299.90 stock est un entier = 50 category_id est un entier = 3 FIN oAPI.SetURL("https://api.servico-api.com/v1/products ") SI oAPI.POST(, stNovoProduto) ALORS SI oAPI.IsSuccess() ALORS vResposta est un variant = oAPI.ParseJSONResponse() Info("Produto criado com ID: " + vResposta.id) SINON Erreur("Erro ao criar produto: " + oAPI.GetResponseBody()) FIN FIN FIN // ==================== EXEMPLO 7: AUTENTICAÇÃO HMAC ==================== PROCÉDURE ExemploHMACAuth() // Criando instância oAPI est un CLAPI_REST("https://api.pagamento-seguro.com/v2/checkout ") // Configurando autenticação HMAC sAPIKey est une chaîne = "merchant_123456" sAPISecret est une chaîne = "9876543210abcdefghijklmn" oAPI.SetHMACAuth(sAPIKey, sAPISecret) // Preparando dados da transação stCheckout est une Structure amount est un monétaire = 157.99 currency est une chaîne = "BRL" customer est une Structure name est une chaîne = "Maria Oliveira" email est une chaîne = "maria@exemplo.com" phone est une chaîne = "+5511987654321" FIN items est un tableau de Structures sNome est une chaîne nPreco est un monétaire nQuantidade est un entier FIN FIN // Adicionando itens ao checkout stItem1 est une Structure sNome = "Teclado Mecânico" nPreco = 129.99 nQuantidade = 1 FIN Ajoute(stCheckout.items, stItem1) stItem2 est une Structure sNome = "Mouse Gamer" nPreco = 28.00 nQuantidade = 1 FIN Ajoute(stCheckout.items, stItem2) // Enviando requisição com HMAC SI oAPI.POST(, stCheckout) ALORS SI oAPI.IsSuccess() ALORS vResposta est un variant = oAPI.ParseJSONResponse() Info("Checkout criado com sucesso!") Info("ID da transação: " + vResposta.transaction_id) Info("URL de pagamento: " + vResposta.payment_url) SINON Erreur("Erro no checkout: " + oAPI.GetStatusCode()) Erreur("Detalhes: " + oAPI.GetResponseBody()) FIN FIN FIN // ==================== EXEMPLO 8: AUTENTICAÇÃO AWS ==================== PROCÉDURE ExemploAWSAuth() // Criando instância oAPI est un CLAPI_REST("https://s3.amazonaws.com/my-bucket/file.txt ") // Configurando autenticação AWS sAccessKey est une chaîne = "AKIAIOSFODNN7EXAMPLE" sSecretKey est une chaîne = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" sRegion est une chaîne = "us-east-1" sService est une chaîne = "s3" oAPI.SetAWSAuth(sAccessKey, sSecretKey, sRegion, sService) // Upload de arquivo para a AWS S3 oAPI.SetMethod("PUT") oAPI.SetContentType("text/plain") oAPI.SetRequestBody("Conteúdo do arquivo de texto para upload no S3") SI oAPI.Send() ALORS SI oAPI.IsSuccess() ALORS Info("Arquivo enviado com sucesso para o S3!") SINON Erreur("Erro no upload: " + oAPI.GetStatusCode()) Erreur("Detalhes: " + oAPI.GetResponseBody()) FIN FIN // Listando objetos do bucket oAPI.SetURL("https://s3.amazonaws.com/my-bucket ") oAPI.SetMethod("GET") SI oAPI.Send() ALORS SI oAPI.IsSuccess() ALORS // A resposta é XML, vamos usar o parser XML xmlResp est un xmlDocument = oAPI.ParseXMLResponse() SI xmlResp <> Null ALORS // Processando XML do S3 sXPath est une chaîne = "//Contents/Key" tabArquivos est un tableau de chaînes = XMLExtraitChaîne(xmlResp, sXPath) Info("Arquivos no bucket:") POUR TOUT arquivo DE tabArquivos Trace(" - " + arquivo) FIN FIN SINON Erreur("Erro ao listar bucket: " + oAPI.GetStatusCode()) FIN FIN FIN // ==================== EXEMPLO 9: UPLOAD DE ARQUIVOS ==================== PROCÉDURE ExemploUploadArquivo() // Criando instância oAPI est un CLAPI_REST("https://api.sistema-documentos.com/upload ") // Configurando autenticação básica oAPI.SetBasicAuth("sistema_upload", "senha123") // Adicionando arquivos para upload sCaminhoArquivo1 est une chaîne = fRepExe() + [fsep] + "docs" + [fsep] + "relatorio.pdf" sCaminhoArquivo2 est une chaîne = fRepExe() + [fsep] + "docs" + [fsep] + "anexo.xlsx" oAPI.AddFile("documento", sCaminhoArquivo1) oAPI.AddFile("anexo", sCaminhoArquivo2) // Adicionando metadados do upload oAPI.AddFormData("descricao", "Relatório financeiro trimestral") oAPI.AddFormData("departamento", "Financeiro") oAPI.AddFormData("categoria", "Relatório") // Realizando upload multipart/form-data SI oAPI.POST() ALORS SI oAPI.IsSuccess() ALORS vResposta est un variant = oAPI.ParseJSONResponse() Info("Upload realizado com sucesso!") Info("ID do documento: " + vResposta.document_id) POUR TOUT arquivo DE vResposta.files Trace("Arquivo: " + arquivo.name + " - URL: " + arquivo.url) FIN SINON Erreur("Erro no upload: " + oAPI.GetStatusCode()) FIN FIN FIN // ==================== EXEMPLO 10: DOWNLOAD DE ARQUIVO ==================== PROCÉDURE ExemploDownloadArquivo() // Criando instância oAPI est un CLAPI_REST("https://api.sistema-documentos.com/files/12345/download ") // Configurando autenticação token oAPI.SetBearerAuth("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFjZXNzbyBEb3dubG9hZCIsImlhdCI6MTUxNjIzOTAyMn0.sbbCA_SYB1fK-OPv7P_3BTQqaZuE454OgtgXd7SckJs") // Configurando timeout maior para download oAPI.SetTimeout(180) // 3 minutos // Realizando requisição GET SI oAPI.GET() ALORS SI oAPI.IsSuccess() ALORS // Obtendo o nome do arquivo do cabeçalho sNomeArquivo est une chaîne = oAPI.GetResponseHeader("Content-Disposition") // Extraindo nome do arquivo (simplificado) sNomeArquivo = Remplace(sNomeArquivo, "attachment; filename=", "") sNomeArquivo = Remplace(sNomeArquivo, """", "") // Salvando o arquivo sCaminhoDestino est une chaîne = fRepExe() + [fsep] + "downloads" + [fsep] + sNomeArquivo // Criando diretório se não existir sDirDownload est une chaîne = fRepExe() + [fsep] + "downloads" SI fRépertoireExiste(sDirDownload) = Faux ALORS frCrée(sDirDownload) FIN // Salvando o conteúdo fSauveBuffer(sCaminhoDestino, oAPI.GetResponseBody()) Info("Arquivo baixado com sucesso para: " + sCaminhoDestino) SINON Erreur("Erro ao baixar arquivo: " + oAPI.GetStatusCode()) FIN FIN FIN // ==================== EXEMPLO 11: REQUISIÇÃO COM CACHE E RETRY ==================== PROCÉDURE ExemploCacheRetry() // Criando instância oAPI est un CLAPI_REST("https://api.servico-com-problemas.com/dados ") // Configurando política de retry oAPI.SetRetryPolicy(5, 3000) // 5 tentativas, 3 segundos entre cada // Configurando cache oAPI.EnableCache(Vrai, 600) // Cache de 10 minutos // Configurando logging para debug oAPI.EnableLogging(Vrai, "retry_log.txt", 4) // Realizando requisição Trace("Iniciando requisição com política de retry...") SI oAPI.GET() ALORS SI oAPI.IsSuccess() ALORS Info("Requisição bem-sucedida após retries!") vResposta est un variant = oAPI.ParseJSONResponse() // Processando resposta Trace("Dados obtidos: " + vResposta.titulo) SINON Erreur("Todos os retries falharam: " + oAPI.GetStatusCode()) FIN FIN // Nova requisição que usará o cache Trace("Realizando segunda requisição (deve usar cache)...") oAPI2 est un CLAPI_REST("https://api.servico-com-problemas.com/dados ") oAPI2.EnableCache(Vrai, 600) SI oAPI2.GET() ALORS Trace("Requisição do cache concluída: " + oAPI2.GetStatusCode()) FIN FIN // ==================== EXEMPLO 12: SIMULADOR DE API DE PAGAMENTO ==================== PROCÉDURE SimuladorPagamento() // Função que simula uma API de pagamento completo // para demonstrar o uso avançado da classe // -------- 1. Autenticação e obtenção de token -------- // Primeiro obtemos um token de acesso oAuth est un CLAPI_REST("https://api.pagamento-xyz.com/auth/token ") oAuth.SetBasicAuth("merchant_id_123", "merchant_secret_xyz") oAuth.SetContentType("application/x-www-form-urlencoded") oAuth.AddFormData("grant_type", "client_credentials") sToken est une chaîne = "" Info("1. Obtendo token de acesso...") SI oAuth.POST() ET oAuth.IsSuccess() ALORS vToken est un variant = oAuth.ParseJSONResponse() sToken = vToken.access_token Info(" - Token obtido com sucesso") SINON Erreur("Falha na autenticação: " + oAuth.GetResponseBody()) RETOUR FIN // -------- 2. Verificação de cliente -------- // Verificando se o cliente existe oCPF est un CLAPI_REST("https://api.pagamento-xyz.com/customers/verify ") oCPF.SetBearerAuth(sToken) // Parâmetros da verificação oCPF.AddQueryParam("document", "123.456.789-00") oCPF.AddQueryParam("type", "CPF") Info("2. Verificando cliente...") SI oCPF.GET() ET oCPF.IsSuccess() ALORS vCliente est un variant = oCPF.ParseJSONResponse() SI vCliente.exists = Vrai ALORS Info(" - Cliente encontrado: " + vCliente.name) SINON Info(" - Cliente não encontrado, vamos cadastrá-lo") // 2.1 - Cadastrando novo cliente oCadastro est un CLAPI_REST("https://api.pagamento-xyz.com/customers ") oCadastro.SetBearerAuth(sToken) stNovoCliente est une Structure name est une chaîne = "Maria Souza" email est une chaîne = "maria@exemplo.com" document est une chaîne = "123.456.789-00" document_type est une chaîne = "CPF" phone est une chaîne = "+5511987654321" address est une Structure street est une chaîne = "Av. Paulista" number est une chaîne = "1000" city est une chaîne = "São Paulo" state est une chaîne = "SP" postal_code est une chaîne = "01310-100" country est une chaîne = "BR" FIN FIN SI oCadastro.POST(, stNovoCliente) ET oCadastro.IsSuccess() ALORS vNovoCliente est un variant = oCadastro.ParseJSONResponse() Info(" - Cliente cadastrado com ID: " + vNovoCliente.id) SINON Erreur("Falha ao cadastrar cliente: " + oCadastro.GetResponseBody()) RETOUR FIN FIN SINON Erreur("Falha na verificação de cliente: " + oCPF.GetResponseBody()) RETOUR FIN // -------- 3. Criação de pagamento -------- Info("3. Criando transação de pagamento...") oTransacao est un CLAPI_REST("https://api.pagamento-xyz.com/transactions ") oTransacao.SetBearerAuth(sToken) // Preparando dados da transação stTransacao est une Structure amount est un monétaire = 156.78 currency est une chaîne = "BRL" description est une chaîne = "Compra na Loja XYZ" customer_id est une chaîne = "cust_123456" // ID do cliente real seria retornado na etapa anterior payment est une Structure method est une chaîne = "credit_card" installments est un entier = 3 capture est un booléen = Vrai card est une Structure number est une chaîne = "4111111111111111" holder_name est une chaîne = "MARIA SOUZA" expiration_month est un entier = 12 expiration_year est un entier = 2025 cvv est une chaîne = "123" FIN FIN FIN SI oTransacao.POST(, stTransacao) ET oTransacao.IsSuccess() ALORS vResultado est un variant = oTransacao.ParseJSONResponse() Info(" - Transação criada com sucesso!") Info(" - ID: " + vResultado.id) Info(" - Status: " + vResultado.status) // -------- 4. Consultando status da transação -------- Info("4. Consultando status da transação...") oStatus est un CLAPI_REST("https://api.pagamento-xyz.com/transactions/ " + vResultado.id) oStatus.SetBearerAuth(sToken) SI oStatus.GET() ET oStatus.IsSuccess() ALORS vStatus est un variant = oStatus.ParseJSONResponse() Info(" - Status detalhado: " + vStatus.status) Info(" - Data processamento: " + vStatus.processed_at) Info(" - Gateway: " + vStatus.acquirer) // -------- 5. Gerando recibo -------- SI vStatus.status = "approved" ALORS Info("5. Gerando recibo da transação...") oRecibo est un CLAPI_REST("https://api.pagamento-xyz.com/transactions/ " + vResultado.id + "/receipt") oRecibo.SetBearerAuth(sToken) oRecibo.AddQueryParam("format", "pdf") SI oRecibo.GET() ET oRecibo.IsSuccess() ALORS // Salvando o PDF do recibo sCaminhoRecibo est une chaîne = fRepExe() + [fsep] + "recibos" + [fsep] + "recibo_" + vResultado.id + ".pdf" // Criando diretório se não existir sDirRecibos est une chaîne = fRepExe() + [fsep] + "recibos" SI fRépertoireExiste(sDirRecibos) = Faux ALORS frCrée(sDirRecibos) FIN // Salvando o recibo fSauveBuffer(sCaminhoRecibo, oRecibo.GetResponseBody()) Info(" - Recibo salvo em: " + sCaminhoRecibo) SINON Erreur("Falha ao gerar recibo: " + oRecibo.GetStatusCode()) FIN FIN SINON Erreur("Falha ao consultar status: " + oStatus.GetResponseBody()) FIN SINON Erreur("Falha ao criar transação: " + oTransacao.GetResponseBody()) FIN Info("Simulação de pagamento concluída!") FIN // ==================== EXEMPLO 13: INTEGRAÇÃO COM API DE CLIMA ==================== PROCÉDURE ExemploAPIClima() // Demonstração de integração com API de clima OpenWeatherMap // Criando instância oClima est un CLAPI_REST("https://api.openweathermap.org/data/2.5/weather ") // Usando autenticação via API Key como parâmetro (comum em APIs de clima) sAPIKey est une chaîne = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" oClima.AddQueryParam("appid", sAPIKey) // Adicionando parâmetros da consulta oClima.AddQueryParam("q", "São Paulo,BR") oClima.AddQueryParam("units", "metric") // Temperatura em Celsius oClima.AddQueryParam("lang", "pt_br") // Descrições em português // Habilitando cache para economizar requisições oClima.EnableCache(Vrai, 3600) // Cache de 1 hora // Realizando a consulta Info("Consultando dados climáticos...") SI oClima.GET() ALORS SI oClima.IsSuccess() ALORS vClima est un variant = oClima.ParseJSONResponse() Info("Clima atual em " + vClima.name + ", " + vClima.sys.country) Info("Temperatura: " + vClima.main.temp + "°C") Info("Sensação térmica: " + vClima.main.feels_like + "°C") Info("Umidade: " + vClima.main.humidity + "%") Info("Condição: " + vClima.weather[1].description) // Salvando os dados em um arquivo JSON sArquivoJSON est une chaîne = fRepExe() + [fsep] + "clima_" + vClima.name + ".json" fSauveTexte(sArquivoJSON, oClima.GetResponseBody()) // Consultando previsão de 5 dias oPrevisao est un CLAPI_REST("https://api.openweathermap.org/data/2.5/forecast ") oPrevisao.AddQueryParam("appid", sAPIKey) oPrevisao.AddQueryParam("q", "São Paulo,BR") oPrevisao.AddQueryParam("units", "metric") oPrevisao.AddQueryParam("lang", "pt_br") oPrevisao.EnableCache(Vrai, 3600) SI oPrevisao.GET() ET oPrevisao.IsSuccess() ALORS vPrevisao est un variant = oPrevisao.ParseJSONResponse() Info("Previsão para os próximos dias:") dataAnterior est une chaîne = "" POUR i = 1 A vPrevisao.list..Occurrence dataHora est une chaîne = vPrevisao.list[i].dt_txt data est une chaîne = Gauche(dataHora, 10) SI data <> dataAnterior ALORS dataAnterior = data Info("Data: " + data) Info(" - Temperatura: " + vPrevisao.list[i].main.temp + "°C") Info(" - Condição: " + vPrevisao.list[i].weather[1].description) FIN FIN FIN SINON Erreur("Erro ao consultar clima: " + oClima.GetStatusCode()) FIN SINON Erreur("Falha na requisição") FIN FIN // ==================== EXEMPLO 14: MANIPULAÇÃO DE API REST DE E-COMMERCE ==================== PROCÉDURE ExemploECommerce() // Este exemplo demonstra a integração com uma API REST de e-commerce // Token de autenticação (normalmente obtido via login) sToken est une chaîne = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkbWluIFNob3AiLCJpYXQiOjE1MTYyMzkwMjJ9.nF8NXx7CHXdVBCYn7VrPxE_T3hYTOmqDTMEhr9Y-pdw" // -------- 1. Listando categorias -------- Info("1. Consultando categorias...") oCategorias est un CLAPI_REST("https://api.ecommerce.com/v1/categories ") oCategorias.SetBearerAuth(sToken) SI oCategorias.GET() ET oCategorias.IsSuccess() ALORS vCategorias est un variant = oCategorias.ParseJSONResponse() Info("Categorias disponíveis:") POUR TOUT cat DE vCategorias.data Trace(" - " + cat.name + " (ID: " + cat.id + ")") FIN // -------- 2. Listando produtos de uma categoria -------- sCategoriaID est une chaîne = vCategorias.data[1].id Info("2. Consultando produtos da categoria: " + vCategorias.data[1].name) oProdutos est un CLAPI_REST("https://api.ecommerce.com/v1/products ") oProdutos.SetBearerAuth(sToken) oProdutos.AddQueryParam("category_id", sCategoriaID) oProdutos.AddQueryParam("limit", "10") oProdutos.AddQueryParam("sort", "price_asc") SI oProdutos.GET() ET oProdutos.IsSuccess() ALORS vProdutos est un variant = oProdutos.ParseJSONResponse() Info("Produtos encontrados: " + vProdutos.meta.total) POUR TOUT prod DE vProdutos.data Trace(" - " + prod.name + " - R$ " + prod.price) FIN // -------- 3. Detalhes de um produto específico -------- SI vProdutos.data..Occurrence > 0 ALORS sProdutoID est une chaîne = vProdutos.data[1].id Info("3. Consultando detalhes do produto: " + vProdutos.data[1].name) oProdutoDetalhe est un CLAPI_REST("https://api.ecommerce.com/v1/products/ " + sProdutoID) oProdutoDetalhe.SetBearerAuth(sToken) SI oProdutoDetalhe.GET() ET oProdutoDetalhe.IsSuccess() ALORS vProduto est un variant = oProdutoDetalhe.ParseJSONResponse() Info("Detalhes do produto:") Info(" - Nome: " + vProduto.name) Info(" - SKU: " + vProduto.sku) Info(" - Preço: R$ " + vProduto.price) Info(" - Estoque: " + vProduto.stock_quantity) Info(" - Descrição: " + vProduto.description) // -------- 4. Adicionando ao carrinho -------- Info("4. Adicionando produto ao carrinho...") oCarrinho est un CLAPI_REST("https://api.ecommerce.com/v1/cart/items ") oCarrinho.SetBearerAuth(sToken) stItem est une Structure product_id est une chaîne = sProdutoID quantity est un entier = 2 FIN SI oCarrinho.POST(, stItem) ET oCarrinho.IsSuccess() ALORS vCarrinho est un variant = oCarrinho.ParseJSONResponse() Info("Produto adicionado ao carrinho!") Info("ID do carrinho: " + vCarrinho.cart_id) // -------- 5. Consultando carrinho -------- Info("5. Consultando carrinho...") oConsultaCarrinho est un CLAPI_REST("https://api.ecommerce.com/v1/cart ") oConsultaCarrinho.SetBearerAuth(sToken) SI oConsultaCarrinho.GET() ET oConsultaCarrinho.IsSuccess() ALORS vConsultaCarrinho est un variant = oConsultaCarrinho.ParseJSONResponse() Info("Itens no carrinho:") nTotal est un monétaire = 0 POUR TOUT item DE vConsultaCarrinho.items nSubtotal est un monétaire = item.price * item.quantity Trace(" - " + item.product_name + " x" + item.quantity + " = R$ " + nSubtotal) nTotal += nSubtotal FIN Info("Total do carrinho: R$ " + nTotal) // -------- 6. Finalizando compra -------- Info("6. Finalizando compra...") oCheckout est un CLAPI_REST("https://api.ecommerce.com/v1/checkout ") oCheckout.SetBearerAuth(sToken) stCheckout est une Structure cart_id est une chaîne = vCarrinho.cart_id payment est une Structure method est une chaîne = "credit_card" card est une Structure number est une chaîne = "4111111111111111" holder_name est une chaîne = "CLIENTE TESTE" expiry_month est un entier = 12 expiry_year est un entier = 2025 cvv est une chaîne = "123" FIN FIN shipping est une Structure address est une Structure street est une chaîne = "Rua das Flores" number est une chaîne = "123" complement est une chaîne = "Apto 45" neighborhood est une chaîne = "Centro" city est une chaîne = "São Paulo" state est une chaîne = "SP" postal_code est une chaîne = "01234-567" country est une chaîne = "BR" FIN method est une chaîne = "express" FIN FIN SI oCheckout.POST(, stCheckout) ET oCheckout.IsSuccess() ALORS vPedido est un variant = oCheckout.ParseJSONResponse() Info("Pedido finalizado com sucesso!") Info("Número do pedido: " + vPedido.order_number) Info("Status: " + vPedido.status) Info("Previsão de entrega: " + vPedido.shipping.estimated_delivery) SINON Erreur("Erro ao finalizar compra: " + oCheckout.GetResponseBody()) FIN SINON Erreur("Erro ao consultar carrinho: " + oConsultaCarrinho.GetResponseBody()) FIN SINON Erreur("Erro ao adicionar ao carrinho: " + oCarrinho.GetResponseBody()) FIN SINON Erreur("Erro ao obter detalhes do produto: " + oProdutoDetalhe.GetResponseBody()) FIN FIN SINON Erreur("Erro ao listar produtos: " + oProdutos.GetResponseBody()) FIN SINON Erreur("Erro ao listar categorias: " + oCategorias.GetResponseBody()) FIN FIN // ==================== EXEMPLO 15: INTEGRAÇÃO COM API DE TRADUÇÃO ==================== PROCÉDURE ExemploTraducao() // Demonstração de uso da classe com uma API de tradução // Criando instância oTradutor est un CLAPI_REST("https://api.traducao-servico.com/translate ") // Usando autenticação com API Key no header oTradutor.SetAPIKeyAuth("chave_api_secreta_123456", "X-API-Key") // Texto a ser traduzido sTexto est une chaîne = "Bonjour, comment allez-vous? J'espère que vous allez bien." // Configurando o corpo da requisição stTraducao est une Structure text est une chaîne = sTexto source_language est une chaîne = "fr" target_language est une chaîne = "pt" FIN Info("Traduzindo texto do francês para português...") SI oTradutor.POST(, stTraducao) ET oTradutor.IsSuccess() ALORS vResposta est un variant = oTradutor.ParseJSONResponse() Info("Texto original: " + sTexto) Info("Tradução: " + vResposta.translated_text) // Traduzindo para outros idiomas tabIdiomas est un tableau de chaînes = ["en", "es", "it", "de"] tabNomesIdiomas est un tableau associatif de chaînes tabNomesIdiomas["en"] = "inglês" tabNomesIdiomas["es"] = "espanhol" tabNomesIdiomas["it"] = "italiano" tabNomesIdiomas["de"] = "alemão" POUR TOUT idioma DE tabIdiomas stTraducao.target_language = idioma SI oTradutor.POST(, stTraducao) ET oTradutor.IsSuccess() ALORS vResposta = oTradutor.ParseJSONResponse() Info("Tradução para " + tabNomesIdiomas[idioma] + ": " + vResposta.translated_text) SINON Erreur("Erro ao traduzir para " + tabNomesIdiomas[idioma] + ": " + oTradutor.GetResponseBody()) FIN FIN // Detectando idioma oDeteccao est un CLAPI_REST("https://api.traducao-servico.com/detect ") oDeteccao.SetAPIKeyAuth("chave_api_secreta_123456", "X-API-Key") stDeteccao est une Structure text est une chaîne = "Hello world! This is a test." FIN Info("Detectando idioma...") SI oDeteccao.POST(, stDeteccao) ET oDeteccao.IsSuccess() ALORS vDeteccao est un variant = oDeteccao.ParseJSONResponse() Info("Texto: " + stDeteccao.text) Info("Idioma detectado: " + vDeteccao.language) Info("Confiança: " + vDeteccao.confidence + "%") SINON Erreur("Erro na detecção de idioma: " + oDeteccao.GetResponseBody()) FIN SINON Erreur("Erro na tradução: " + oTradutor.GetResponseBody()) FIN FIN // ==================== EXEMPLO COMPLETO: APLICAÇÃO CRUD ==================== PROCÉDURE ExemploCRUDCompleto() // Este exemplo simula uma aplicação CRUD completa usando a classe CLAPI_REST // URL base da API sURLBase est une chaîne = "https://api.empresa-xyz.com/v1 " // Token de autenticação sToken est une chaîne = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsIm5hbWUiOiJBZG1pbmlzdHJhZG9yIiwiaWF0IjoxNTE2MjM5MDIyfQ.L7HM_43sYQTbV8NG9iBH0B2R4nRfbXAAmD4zKJwIW5Y" // Instância base da API para reutilização oAPIBase est un CLAPI_REST() oAPIBase.SetBearerAuth(sToken) oAPIBase.EnableLogging(Vrai, "crud_log.txt", 3) oAPIBase.SetTimeout(30) oAPIBase.SetRetryPolicy(3, 2000) // ------- CREATE: Criando um novo funcionário ------- stNovoFuncionario est une Structure nome est une chaîne = "Carlos Alberto Silva" cargo est une chaîne = "Desenvolvedor Sênior" departamento est une chaîne = "TI" email est une chaîne = "carlos.silva@empresa-xyz.com" salario est un monétaire = 7500.00 data_admissao est une chaîne = DateSys() // Formato ISO 8601 FIN Info("1. CREATE - Criando novo funcionário...") oCreate est un CLAPI_REST(ChaîneConstruit("%1/funcionarios", sURLBase)) oCreate.SetBearerAuth(sToken) oCreate.EnableLogging(Vrai, "crud_log.txt", 3) SI oCreate.POST(, stNovoFuncionario) ET oCreate.IsSuccess() ALORS vFuncionario est un variant = oCreate.ParseJSONResponse() Info("Funcionário criado com sucesso!") Info("ID: " + vFuncionario.id) // ------- READ: Consultando o funcionário criado ------- sFuncionarioID est une chaîne = vFuncionario.id Info("2. READ - Consultando funcionário ID: " + sFuncionarioID) oRead est un CLAPI_REST(ChaîneConstruit("%1/funcionarios/%2", sURLBase, sFuncionarioID)) oRead.SetBearerAuth(sToken) SI oRead.GET() ET oRead.IsSuccess() ALORS vDetalhes est un variant = oRead.ParseJSONResponse() Info("Detalhes do funcionário:") Info(" - Nome: " + vDetalhes.nome) Info(" - Cargo: " + vDetalhes.cargo) Info(" - Departamento: " + vDetalhes.departamento) Info(" - Email: " + vDetalhes.email) Info(" - Salário: R$ " + vDetalhes.salario) Info(" - Data de Admissão: " + vDetalhes.data_admissao) // ------- UPDATE: Atualizando informações do funcionário ------- Info("3. UPDATE - Atualizando funcionário...") stAtualizacao est une Structure id est une chaîne = sFuncionarioID cargo est une chaîne = "Líder Técnico" salario est un monétaire = 8500.00 departamento est une chaîne = "TI - Desenvolvimento" FIN oUpdate est un CLAPI_REST(ChaîneConstruit("%1/funcionarios/%2", sURLBase, sFuncionarioID)) oUpdate.SetBearerAuth(sToken) SI oUpdate.PATCH(, stAtualizacao) ET oUpdate.IsSuccess() ALORS vAtualizado est un variant = oUpdate.ParseJSONResponse() Info("Funcionário atualizado com sucesso!") Info(" - Novo cargo: " + vAtualizado.cargo) Info(" - Novo salário: R$ " + vAtualizado.salario) Info(" - Novo departamento: " + vAtualizado.departamento) // ------- READ LIST: Listando todos os funcionários ------- Info("4. READ LIST - Listando todos os funcionários...") oList est un CLAPI_REST(ChaîneConstruit("%1/funcionarios", sURLBase)) oList.SetBearerAuth(sToken) oList.AddQueryParam("limit", "10") oList.AddQueryParam("page", "1") oList.AddQueryParam("sort", "nome,asc") SI oList.GET() ET oList.IsSuccess() ALORS vLista est un variant = oList.ParseJSONResponse() Info("Total de funcionários: " + vLista.meta.total) Info("Página: " + vLista.meta.page + " de " + vLista.meta.total_pages) Info("Listagem de funcionários:") POUR TOUT func DE vLista.data Trace(" - " + func.nome + " (" + func.cargo + ") - Depto: " + func.departamento) FIN // ------- DELETE: Removendo o funcionário ------- Info("5. DELETE - Removendo funcionário ID: " + sFuncionarioID) oDelete est un CLAPI_REST(ChaîneConstruit("%1/funcionarios/%2", sURLBase, sFuncionarioID)) oDelete.SetBearerAuth(sToken) SI oDelete.DELETE() ET oDelete.IsSuccess() ALORS Info("Funcionário removido com sucesso!") // Verificando se foi realmente removido oVerify est un CLAPI_REST(ChaîneConstruit("%1/funcionarios/%2", sURLBase, sFuncionarioID)) oVerify.SetBearerAuth(sToken) SI oVerify.GET() ALORS SI oVerify.GetStatusCode() = 404 ALORS Info("Confirmado: funcionário não existe mais.") SINON Erreur("Erro de verificação: o funcionário ainda existe!") FIN FIN SINON Erreur("Erro ao remover funcionário: " + oDelete.GetResponseBody()) FIN SINON Erreur("Erro ao listar funcionários: " + oList.GetResponseBody()) FIN SINON Erreur("Erro ao atualizar funcionário: " + oUpdate.GetResponseBody()) FIN SINON Erreur("Erro ao consultar funcionário: " + oRead.GetResponseBody()) FIN SINON Erreur("Erro ao criar funcionário: " + oCreate.GetResponseBody()) FIN Info("Processo CRUD completo finalizado!") FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 03 2025 - 9:34 AM
Vou continuar os exemplos da classe CLAPI_REST v4.0 a partir de onde parou. A classe CLAPI_REST versão 4.0 foi criada com sucesso e agora os exemplos estão completos. Esta implementação oferece uma solução robusta para integração com APIs REST usando WLanguage. A classe incorpora diversas melhorias em relação à versão do fórum, incluindo: ### Recursos principais da classe CLAPI_REST v4.0: 1. **Suporte completo a vários métodos de autenticação:** - Basic Auth (usuário/senha) - Bearer Token - API Key (em header ou query param) - JWT - Certificados SSL - OAuth 2.0 Client Credentials - HMAC - AWS Signature v4 2. **Funcionalidades avançadas:** - Sistema de cache para economizar requisições - Política de retry automático - Logging detalhado - Suporte a upload de arquivos - Manipulação de diferentes tipos de conteúdo - Parsers JSON e XML integrados 3. **Facilidade de uso:** - Métodos de conveniência para operações comuns (GET, POST, PUT, etc.) - Métodos para verificar status e manipular respostas - Configurações flexíveis de timeout e SSL ### Exemplos de uso demonstrados: Os 15 exemplos completos mostram como utilizar a classe em cenários reais, incluindo: - Autenticação básica e operações CRUD - Autenticação com token Bearer e JWT - Uso de API Key para serviços como previsão do tempo - Autenticação com certificado para serviços bancários - OAuth 2.0 para serviços que requerem tokens de acesso - HMAC para sistemas de pagamento - AWS para integração com serviços da Amazon - Upload e download de arquivos - Uso de cache e políticas de retry - Exemplo de fluxo completo de e-commerce - Integração com APIs de tradução - Simulador de API de pagamento - Aplicação CRUD completa Esta classe oferece uma solução completa para qualquer desenvolvedor WinDev que precise realizar integrações com APIs REST, independentemente do tipo de autenticação ou complexidade da API. Você pode utilizar a classe e os exemplos como referência para suas integrações, adaptando conforme necessário para seus casos de uso específicos. -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 1:48 AM
// ------------------------------------------------------------------------------------------------ // Classe RestAPI v5.0 // ------------------------------------------------------------------------------------------------ // Desenvolvida para simplificar e padronizar o consumo de APIs REST em aplicações WinDev // Suporta múltiplos métodos de autenticação e tipos de requisição // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ // Enumerações utilizadas pela classe ENUMERATION ERestMethod GET = 1 POST = 2 PUT = 3 DELETE = 4 PATCH = 5 HEAD = 6 OPTIONS = 7 FIN ENUMERATION EAuthType NONE = 0 // Sem autenticação BASIC = 1 // Autenticação Básica (usuário:senha) BEARER = 2 // Autenticação com token JWT (Bearer) API_KEY = 3 // Autenticação com chave de API OAUTH2 = 4 // Autenticação OAuth 2.0 DIGEST = 5 // Autenticação Digest HMAC = 6 // Autenticação HMAC CUSTOM = 7 // Autenticação personalizada FIN ENUMERATION EContentType JSON = 1 // application/json XML = 2 // application/xml FORM = 3 // application/x-www-form-urlencoded MULTIPART = 4 // multipart/form-data TEXT = 5 // text/plain BINARY = 6 // application/octet-stream FIN CLASSE RestAPI // Propriedades base PRIVE _sBaseURL est une chaîne = "" _nTimeout est un entier = 30 _bSSLVerification est un booléen = Vrai _sUserAgent est une chaîne = "WinDev RestAPI Client v5.0" _bDebugMode est un booléen = Faux _sLogPath est une chaîne = fRepDonnées() + "\logs" _bAutoRetry est un booléen = Faux _nMaxRetries est un entier = 3 _nRetryDelay est un entier = 1000 // ms // Propriedades de autenticação PRIVE _nAuthType est un EAuthType = EAuthType.NONE _sUsername est une chaîne = "" _sPassword est une chaîne = "" _sToken est une chaîne = "" _sApiKey est une chaîne = "" _sApiKeyName est une chaîne = "" _bApiKeyInHeader est un booléen = Vrai _sOAuth2ClientID est une chaîne = "" _sOAuth2ClientSecret est une chaîne = "" _sOAuth2TokenEndpoint est une chaîne = "" _sOAuth2Scope est une chaîne = "" _sOAuth2RefreshToken est une chaîne = "" _dhOAuth2TokenExpiry est une dateheure _sHMACSecret est une chaîne = "" _sHMACAlgorithm est une chaîne = "SHA256" // Cache e estado PRIVE _tabHeaders est un tableau associatif de chaînes _tabQueryParams est un tableau associatif de chaînes _nDefaultContentType est un EContentType = EContentType.JSON _nResponseCode est un entier = 0 _sLastErrorMessage est une chaîne = "" _sLastResponse est une chaîne = "" _sLastRequest est une chaîne = "" _nCurrentRetryCount est un entier = 0 // Construtores PROCEDURE PUBLIQUE Constructeur() // Inicializa o objeto com valores padrão InitHeaders() FIN PROCEDURE PUBLIQUE Constructeur(sBaseURL est une chaîne) // Inicializa com uma URL base _sBaseURL = sBaseURL InitHeaders() FIN PROCEDURE PUBLIQUE Constructeur(sBaseURL est une chaîne, nAuthType est un EAuthType) // Inicializa com URL base e tipo de autenticação _sBaseURL = sBaseURL _nAuthType = nAuthType InitHeaders() FIN // Métodos de configuração geral PROCEDURE PUBLIQUE SetBaseURL(sURL est une chaîne) _sBaseURL = sURL FIN PROCEDURE PUBLIQUE SetTimeout(nSeconds est un entier) _nTimeout = nSeconds FIN PROCEDURE PUBLIQUE SetSSLVerification(bVerify est un booléen) _bSSLVerification = bVerify FIN PROCEDURE PUBLIQUE SetUserAgent(sUserAgent est une chaîne) _sUserAgent = sUserAgent _tabHeaders["User-Agent"] = _sUserAgent FIN PROCEDURE PUBLIQUE SetContentType(nContentType est un EContentType) _nDefaultContentType = nContentType SELON _nDefaultContentType CAS EContentType.JSON: _tabHeaders["Content-Type"] = "application/json" CAS EContentType.XML: _tabHeaders["Content-Type"] = "application/xml" CAS EContentType.FORM: _tabHeaders["Content-Type"] = "application/x-www-form-urlencoded" CAS EContentType.MULTIPART: _tabHeaders["Content-Type"] = "multipart/form-data" CAS EContentType.TEXT: _tabHeaders["Content-Type"] = "text/plain" CAS EContentType.BINARY: _tabHeaders["Content-Type"] = "application/octet-stream" FIN FIN PROCEDURE PUBLIQUE SetDebugMode(bDebug est un booléen, sLogPath est une chaîne = "") _bDebugMode = bDebug SI sLogPath <> "" ALORS _sLogPath = sLogPath FIN // Cria diretório de logs se não existir SI _bDebugMode ET PAS fRepertoireExiste(_sLogPath) ALORS fRepCrée(_sLogPath) FIN FIN PROCEDURE PUBLIQUE SetAutoRetry(bAutoRetry est un booléen, nMaxRetries est un entier = 3, nRetryDelay est un entier = 1000) _bAutoRetry = bAutoRetry _nMaxRetries = nMaxRetries _nRetryDelay = nRetryDelay FIN // Configuração de cabeçalhos e parâmetros PROCEDURE PUBLIQUE AddHeader(sName est une chaîne, sValue est une chaîne) _tabHeaders[sName] = sValue FIN PROCEDURE PUBLIQUE RemoveHeader(sName est une chaîne) SI _tabHeaders[sName]..Existe ALORS TableauSupprime(_tabHeaders, sName) FIN FIN PROCEDURE PUBLIQUE ClearHeaders() TableauSupprimeTout(_tabHeaders) InitHeaders() FIN PROCEDURE PUBLIQUE AddQueryParam(sName est une chaîne, sValue est une chaîne) _tabQueryParams[sName] = sValue FIN PROCEDURE PUBLIQUE RemoveQueryParam(sName est une chaîne) SI _tabQueryParams[sName]..Existe ALORS TableauSupprime(_tabQueryParams, sName) FIN FIN PROCEDURE PUBLIQUE ClearQueryParams() TableauSupprimeTout(_tabQueryParams) FIN // Métodos de configuração de autenticação PROCEDURE PUBLIQUE SetAuthNone() _nAuthType = EAuthType.NONE // Remove cabeçalhos relacionados à autenticação RemoveHeader("Authorization") FIN PROCEDURE PUBLIQUE SetAuthBasic(sUsername est une chaîne, sPassword est une chaîne) _nAuthType = EAuthType.BASIC _sUsername = sUsername _sPassword = sPassword // Configura o cabeçalho de autorização _tabHeaders["Authorization"] = "Basic " + Encode(sUsername + ":" + sPassword, encodeBASE64) FIN PROCEDURE PUBLIQUE SetAuthBearer(sToken est une chaîne) _nAuthType = EAuthType.BEARER _sToken = sToken // Configura o cabeçalho de autorização _tabHeaders["Authorization"] = "Bearer " + sToken FIN PROCEDURE PUBLIQUE SetAuthApiKey(sApiKey est une chaîne, sApiKeyName est une chaîne, bInHeader est un booléen = Vrai) _nAuthType = EAuthType.API_KEY _sApiKey = sApiKey _sApiKeyName = sApiKeyName _bApiKeyInHeader = bInHeader // Configura o cabeçalho ou parâmetro para a chave de API SI _bApiKeyInHeader ALORS _tabHeaders[_sApiKeyName] = _sApiKey SINON _tabQueryParams[_sApiKeyName] = _sApiKey FIN FIN PROCEDURE PUBLIQUE SetAuthOAuth2(sClientID est une chaîne, sClientSecret est une chaîne, sTokenEndpoint est une chaîne, sScope est une chaîne = "") _nAuthType = EAuthType.OAUTH2 _sOAuth2ClientID = sClientID _sOAuth2ClientSecret = sClientSecret _sOAuth2TokenEndpoint = sTokenEndpoint _sOAuth2Scope = sScope FIN PROCEDURE PUBLIQUE SetAuthDigest(sUsername est une chaîne, sPassword est une chaîne) _nAuthType = EAuthType.DIGEST _sUsername = sUsername _sPassword = sPassword // A autenticação digest é processada durante a chamada FIN PROCEDURE PUBLIQUE SetAuthHMAC(sSecret est une chaîne, sAlgorithm est une chaîne = "SHA256") _nAuthType = EAuthType.HMAC _sHMACSecret = sSecret _sHMACAlgorithm = sAlgorithm FIN PROCEDURE PUBLIQUE SetCustomAuth(sAuthHeaderValue est une chaîne) _nAuthType = EAuthType.CUSTOM _tabHeaders["Authorization"] = sAuthHeaderValue FIN // Métodos para obter tokens OAuth2 FONCTION PUBLIQUE RefreshOAuth2Token() : Booléen // Verifica se a configuração OAuth2 foi feita SI _nAuthType <> EAuthType.OAUTH2 OU _sOAuth2ClientID = "" OU _sOAuth2ClientSecret = "" OU _sOAuth2TokenEndpoint = "" ALORS _sLastErrorMessage = "Configuração OAuth2 incompleta" RENVOYER Faux FIN LOCAL restHttp est un restRequête LOCAL sGrantType est une chaîne = "client_credentials" SI _sOAuth2RefreshToken <> "" ALORS sGrantType = "refresh_token" FIN // Construção da requisição para obter token restHttp.URL = _sOAuth2TokenEndpoint restHttp.Méthode = httpPost restHttp.ContentType = "application/x-www-form-urlencoded" // Parâmetros para obtenção do token restHttp.Paramètre["grant_type"] = sGrantType restHttp.Paramètre["client_id"] = _sOAuth2ClientID restHttp.Paramètre["client_secret"] = _sOAuth2ClientSecret SI _sOAuth2Scope <> "" ALORS restHttp.Paramètre["scope"] = _sOAuth2Scope FIN SI sGrantType = "refresh_token" ALORS restHttp.Paramètre["refresh_token"] = _sOAuth2RefreshToken FIN // Executar a requisição SI httpEnvoie(restHttp) = Faux ALORS _sLastErrorMessage = "Falha ao obter token OAuth2: " + ErreurInfo(errMessage) RENVOYER Faux FIN // Processar a resposta em JSON LOCAL oResponse est un JSON = restHttp.Réponse SI ErreurDétectée ALORS _sLastErrorMessage = "Falha ao processar resposta OAuth2: " + ErreurInfo(errMessage) RENVOYER Faux FIN // Verificar se obteve o token SI oResponse.access_token <> Null ALORS _sToken = oResponse.access_token _tabHeaders["Authorization"] = "Bearer " + _sToken // Verificar se há refresh token SI oResponse.refresh_token <> Null ALORS _sOAuth2RefreshToken = oResponse.refresh_token FIN // Verificar se há informação de expiração SI oResponse.expires_in <> Null ALORS LOCAL nExpiresIn est un entier = oResponse.expires_in _dhOAuth2TokenExpiry = DateHeureSys() + nExpiresIn FIN RENVOYER Vrai SINON _sLastErrorMessage = "Resposta OAuth2 não contém access_token" RENVOYER Faux FIN FIN FONCTION PUBLIQUE IsOAuth2TokenExpired() : Booléen // Verifica se o token está expirado SI _nAuthType <> EAuthType.OAUTH2 OU _sToken = "" ALORS RENVOYER Vrai FIN SI DateHeureSys() > _dhOAuth2TokenExpiry ALORS RENVOYER Vrai FIN RENVOYER Faux FIN // Métodos para geração de assinatura HMAC PRIVE FONCTION GetHMACSignature(sMethod est une chaîne, sUrl est une chaîne, sData est une chaîne, sNonce est une chaîne = "") : chaîne // Gera um timestamp LOCAL sTimestamp est une chaîne = NumériqueVersChaine(DateHeureVersEntier(DateHeureSys())) // Define um nonce se não for fornecido SI sNonce = "" ALORS sNonce = ProcédureVersChaine(AleatoireEntre(10000,99999)) FIN // Constrói a string a ser assinada LOCAL sStringToSign est une chaîne sStringToSign = sMethod + "\n" sStringToSign += sUrl + "\n" sStringToSign += sTimestamp + "\n" sStringToSign += sNonce + "\n" SI sData <> "" ALORS // Para dados JSON, poderia ordenar as chaves antes de assinar sStringToSign += sData FIN // Gera a assinatura HMAC LOCAL sSignature est une chaîne SELON _sHMACAlgorithm CAS "SHA256": sSignature = HMACsha256(sStringToSign, _sHMACSecret) CAS "SHA512": sSignature = HMACsha512(sStringToSign, _sHMACSecret) CAS "MD5": sSignature = HMACmd5(sStringToSign, _sHMACSecret) AUTRE CAS: sSignature = HMACsha256(sStringToSign, _sHMACSecret) // Padrão FIN // Adiciona os headers necessários _tabHeaders["X-Timestamp"] = sTimestamp _tabHeaders["X-Nonce"] = sNonce _tabHeaders["X-Signature"] = sSignature RENVOYER sSignature FIN PRIVE FONCTION HMACsha256(sInput est une chaîne, sKey est une chaîne) : chaîne // Implementação simplificada usando as funções do WinDev // Em uma implementação real, usaria CryptoHashSignature RENVOYER Encode(HashChaîne(cryptAlgoSHA256, sInput, sKey), encodeBASE64) FIN PRIVE FONCTION HMACsha512(sInput est une chaîne, sKey est une chaîne) : chaîne RENVOYER Encode(HashChaîne(cryptAlgoSHA512, sInput, sKey), encodeBASE64) FIN PRIVE FONCTION HMACmd5(sInput est une chaîne, sKey est une chaîne) : chaîne RENVOYER Encode(HashChaîne(cryptAlgoMD5, sInput, sKey), encodeBASE64) FIN // Métodos para requisições HTTP FONCTION PUBLIQUE GET(sEndpoint est une chaîne, tabParams est un tableau associatif de chaînes = []) : JSON ClearQueryParams() POUR TOUT pParam DE tabParams AddQueryParam(pParam..Nom, pParam) FIN LOCAL resultat est un JSON = ExecuteRequest(ERestMethod.GET, sEndpoint) RENVOYER resultat FIN FONCTION PUBLIQUE POST(sEndpoint est une chaîne, vData est un variant) : JSON LOCAL resultat est un JSON = ExecuteRequest(ERestMethod.POST, sEndpoint, vData) RENVOYER resultat FIN FONCTION PUBLIQUE PUT(sEndpoint est une chaîne, vData est un variant) : JSON LOCAL resultat est un JSON = ExecuteRequest(ERestMethod.PUT, sEndpoint, vData) RENVOYER resultat FIN FONCTION PUBLIQUE DELETE(sEndpoint est une chaîne, vData est un variant = Null) : JSON LOCAL resultat est un JSON = ExecuteRequest(ERestMethod.DELETE, sEndpoint, vData) RENVOYER resultat FIN FONCTION PUBLIQUE PATCH(sEndpoint est une chaîne, vData est un variant) : JSON LOCAL resultat est un JSON = ExecuteRequest(ERestMethod.PATCH, sEndpoint, vData) RENVOYER resultat FIN FONCTION PUBLIQUE OPTIONS(sEndpoint est une chaîne) : JSON LOCAL resultat est un JSON = ExecuteRequest(ERestMethod.OPTIONS, sEndpoint) RENVOYER resultat FIN FONCTION PUBLIQUE HEAD(sEndpoint est une chaîne) : Booléen LOCAL resultat est un JSON = ExecuteRequest(ERestMethod.HEAD, sEndpoint) RENVOYER (_nResponseCode >= 200 ET _nResponseCode < 300) FIN // Métodos para download e upload de arquivos FONCTION PUBLIQUE DownloadFile(sEndpoint est une chaîne, sLocalPath est une chaîne) : Booléen // Verificar se a URL base e endpoint são válidos SI _sBaseURL = "" OU sEndpoint = "" ALORS _sLastErrorMessage = "URL base ou endpoint inválido" RENVOYER Faux FIN // Montar URL completa LOCAL sFullURL est une chaîne = BuildFullURL(sEndpoint) // Requisição HTTP para download LOCAL restHttp est un restRequête restHttp.URL = sFullURL restHttp.Méthode = httpGet restHttp.TimeOut = _nTimeout // Configurar cabeçalhos ConfigureHeaders(restHttp) // Configurar verificação SSL restHttp.IgnoreErreurCertificat = PAS _bSSLVerification // Executar download SI httpEnvoie(restHttp) = Faux ALORS _sLastErrorMessage = "Falha no download: " + ErreurInfo(errMessage) LogDebug("ERRO: " + _sLastErrorMessage) RENVOYER Faux FIN // Salvar no arquivo local SI fSauveBuffer(sLocalPath, restHttp.Réponse) = Faux ALORS _sLastErrorMessage = "Falha ao salvar arquivo: " + ErreurInfo(errMessage) LogDebug("ERRO: " + _sLastErrorMessage) RENVOYER Faux FIN _nResponseCode = restHttp.CodeEtat LogDebug("Download concluído: " + sLocalPath + " [" + NumériqueVersChaine(_nResponseCode) + "]") RENVOYER Vrai FIN FONCTION PUBLIQUE UploadFile(sEndpoint est une chaîne, sLocalPath est une chaîne, sFieldName est une chaîne = "file") : JSON // Verificar se a URL base e endpoint são válidos SI _sBaseURL = "" OU sEndpoint = "" OU PAS fFichierExiste(sLocalPath) ALORS _sLastErrorMessage = "URL base, endpoint ou arquivo local inválido" RENVOYER Null FIN // Montar URL completa LOCAL sFullURL est une chaîne = BuildFullURL(sEndpoint) // Requisição HTTP para upload LOCAL restHttp est un restRequête restHttp.URL = sFullURL restHttp.Méthode = httpPost restHttp.TimeOut = _nTimeout // Configurar cabeçalhos (exceto Content-Type que será definido automaticamente) POUR TOUT pHeader DE _tabHeaders SI pHeader..Nom <> "Content-Type" ALORS restHttp.Entête[pHeader..Nom] = pHeader FIN FIN // Configurar verificação SSL restHttp.IgnoreErreurCertificat = PAS _bSSLVerification // Adicionar o arquivo como parâmetro multipart restHttp.Fichier[sFieldName] = sLocalPath // Executar upload SI httpEnvoie(restHttp) = Faux ALORS _sLastErrorMessage = "Falha no upload: " + ErreurInfo(errMessage) LogDebug("ERRO: " + _sLastErrorMessage) RENVOYER Null FIN _nResponseCode = restHttp.CodeEtat _sLastResponse = restHttp.Réponse LogDebug("Upload concluído: " + sLocalPath + " [" + NumériqueVersChaine(_nResponseCode) + "]") // Converter resposta em JSON, se possível LOCAL oResponse est un JSON QUAND EXCEPTION DANS oResponse = JSONVersVariant(_sLastResponse) FAIRE LogDebug("Aviso: Resposta não é um JSON válido") RENVOYER Null FIN RENVOYER oResponse FIN // Métodos auxiliares e principais PRIVE FONCTION ExecuteRequest(nMethod est un ERestMethod, sEndpoint est une chaîne, vData est un variant = Null) : JSON // Reiniciar contador de tentativas _nCurrentRetryCount = 0 // Verificar OAuth2 token e renovar se necessário SI _nAuthType = EAuthType.OAUTH2 ET IsOAuth2TokenExpired() ALORS SI RefreshOAuth2Token() = Faux ALORS LogDebug("ERRO: Falha ao renovar token OAuth2") RENVOYER Null FIN FIN // Tentar executar a requisição LOCAL oResponse est un JSON BOUCLE _nCurrentRetryCount++ // Fazer a requisição oResponse = DoExecuteRequest(nMethod, sEndpoint, vData) // Verificar se precisa repetir SI oResponse <> Null OU PAS _bAutoRetry OU _nCurrentRetryCount > _nMaxRetries ALORS SORTIR FIN // Aguardar antes de repetir Multitâche(1) Temporisation(_nRetryDelay) FIN RENVOYER oResponse FIN PRIVE FONCTION DoExecuteRequest(nMethod est un ERestMethod, sEndpoint est une chaîne, vData est un variant = Null) : JSON // Verificar se a URL base e endpoint são válidos SI _sBaseURL = "" OU sEndpoint = "" ALORS _sLastErrorMessage = "URL base ou endpoint inválido" RENVOYER Null FIN // Montar URL completa com parâmetros query LOCAL sFullURL est une chaîne = BuildFullURL(sEndpoint) // Converter dados para formato adequado LOCAL sData est une chaîne = "" SI vData <> Null ALORS SELON _nDefaultContentType CAS EContentType.JSON: sData = VariantVersJSON(vData) CAS EContentType.XML: SI TypeVar(vData) = typChaîne ALORS sData = vData SINON // Aqui precisaria converter para XML, mas usamos string direta _sLastErrorMessage = "Conversão automática para XML não suportada" RENVOYER Null FIN CAS EContentType.FORM: SI TypeVar(vData) = typTableauAssociatif ALORS POUR TOUT pParam DE vData SI sData <> "" ALORS sData += "&" FIN sData += URLEncode(pParam..Nom) + "=" + URLEncode(pParam) FIN SINON _sLastErrorMessage = "Tipo de dados incompatível com form-urlencoded" RENVOYER Null FIN AUTRE CAS: SI TypeVar(vData) = typChaîne ALORS sData = vData SINON sData = VariantVersJSON(vData) FIN FIN FIN // Configurar assinatura HMAC se necessário SI _nAuthType = EAuthType.HMAC ALORS LOCAL sMethodName est une chaîne = MethodToString(nMethod) GetHMACSignature(sMethodName, sFullURL, sData) FIN // Requisição HTTP LOCAL restHttp est un restRequête restHttp.URL = sFullURL // Configurar método SELON nMethod CAS ERestMethod.GET: restHttp.Méthode = httpGet CAS ERestMethod.POST: restHttp.Méthode = httpPost CAS ERestMethod.PUT: restHttp.Méthode = httpPut CAS ERestMethod.DELETE: restHttp.Méthode = httpDelete CAS ERestMethod.PATCH: restHttp.Méthode = httpPatch CAS ERestMethod.HEAD: restHttp.Méthode = httpHead CAS ERestMethod.OPTIONS: restHttp.Méthode = httpOptions FIN // Configurar timeout restHttp.TimeOut = _nTimeout // Configurar cabeçalhos ConfigureHeaders(restHttp) // Configurar verificação SSL restHttp.IgnoreErreurCertificat = PAS _bSSLVerification // Adicionar dados SI sData <> "" ET nMethod <> ERestMethod.GET ET nMethod <> ERestMethod.HEAD ALORS restHttp.Contenu = sData FIN // Salvar a requisição para debug _sLastRequest = "URL: " + sFullURL + EOT + "Método: " + MethodToString(nMethod) + EOT + "Headers: " + VariantVersJSON(_tabHeaders) + EOT SI sData <> "" ALORS _sLastRequest += "Dados: " + sData FIN LogDebug("REQUEST: " + _sLastRequest) // Executar requisição SI httpEnvoie(restHttp) = Faux ALORS _sLastErrorMessage = "Falha na requisição: " + ErreurInfo(errMessage) LogDebug("ERRO: " + _sLastErrorMessage) RENVOYER Null FIN // Salvar informações da resposta _nResponseCode = restHttp.CodeEtat _sLastResponse = restHttp.Réponse LogDebug("RESPONSE [" + NumériqueVersChaine(_nResponseCode) + "]: " + _sLastResponse) // Verificar resposta HTTP SI _nResponseCode < 200 OU _nResponseCode >= 300 ALORS _sLastErrorMessage = "Erro HTTP " + NumériqueVersChaine(_nResponseCode) + ": " + _sLastResponse RENVOYER Null FIN // Converter resposta em JSON, se possível LOCAL oResponse est un JSON QUAND EXCEPTION DANS oResponse = JSONVersVariant(_sLastResponse) FAIRE LogDebug("Aviso: Resposta não é um JSON válido") RENVOYER Null FIN RENVOYER oResponse FIN PRIVE PROCÉDURE InitHeaders() _tabHeaders["User-Agent"] = _sUserAgent _tabHeaders["Accept"] = "application/json" // Configuração padrão do Content-Type SetContentType(_nDefaultContentType) SetContentType(_nDefaultContentType) FIN PRIVE FONCTION BuildFullURL(sEndpoint est une chaîne) : chaîne // Remove barras duplicadas entre base e endpoint LOCAL sBaseURL est une chaîne = _sBaseURL LOCAL sEndpointURL est une chaîne = sEndpoint SI Droite(sBaseURL, 1) = "/" ET Gauche(sEndpointURL, 1) = "/" ALORS sEndpointURL = Milieu(sEndpointURL, 2) SINON SI Droite(sBaseURL, 1) <> "/" ET Gauche(sEndpointURL, 1) <> "/" ET sEndpointURL <> "" ALORS sEndpointURL = "/" + sEndpointURL FIN // Montar URL completa LOCAL sFullURL est une chaîne = sBaseURL + sEndpointURL // Adicionar parâmetros query, se houver SI TableauOccurrence(_tabQueryParams) > 0 ALORS sFullURL += "?" LOCAL bFirst est un booléen = Vrai POUR TOUT pParam DE _tabQueryParams SI bFirst = Faux ALORS sFullURL += "&" FIN sFullURL += URLEncode(pParam..Nom) + "=" + URLEncode(pParam) bFirst = Faux FIN FIN RENVOYER sFullURL FIN PRIVE PROCÉDURE ConfigureHeaders(restHttp est un restRequête) POUR TOUT pHeader DE _tabHeaders restHttp.Entête[pHeader..Nom] = pHeader FIN FIN PRIVE FONCTION MethodToString(nMethod est un ERestMethod) : chaîne SELON nMethod CAS ERestMethod.GET: RENVOYER "GET" CAS ERestMethod.POST: RENVOYER "POST" CAS ERestMethod.PUT: RENVOYER "PUT" CAS ERestMethod.DELETE: RENVOYER "DELETE" CAS ERestMethod.PATCH: RENVOYER "PATCH" CAS ERestMethod.HEAD: RENVOYER "HEAD" CAS ERestMethod.OPTIONS: RENVOYER "OPTIONS" AUTRE CAS: RENVOYER "GET" FIN FIN PRIVE PROCÉDURE LogDebug(sMessage est une chaîne) SI _bDebugMode ALORS // Obter data e hora atual formatada LOCAL dhNow est une dateheure = DateHeureSys() LOCAL sDateTime est une chaîne = DateHeurePrécise(DateHeureVersChaîne(dhNow, maskDateHeure)) // Definir nome do arquivo de log LOCAL sLogFile est une chaîne = _sLogPath + [fSep] + "restapi_" + DateVersChaîne(dhNow, "yyyyMMdd") + ".log" // Escrever no arquivo de log fAjouteLigne(sLogFile, "[" + sDateTime + "] " + sMessage) FIN FIN // Métodos para acesso a informações de estado FONCTION PUBLIQUE GetLastResponseCode() : entier RENVOYER _nResponseCode FIN FONCTION PUBLIQUE GetLastResponse() : chaîne RENVOYER _sLastResponse FIN FONCTION PUBLIQUE GetLastError() : chaîne RENVOYER _sLastErrorMessage FIN FONCTION PUBLIQUE GetLastRequest() : chaîne RENVOYER _sLastRequest FIN FONCTION PUBLIQUE IsSuccess() : Booléen RENVOYER (_nResponseCode >= 200 ET _nResponseCode < 300) FIN // Método para executar requisições com promessas (async) FONCTION PUBLIQUE ExecuteAsync(nMethod est un ERestMethod, sEndpoint est une chaîne, vData est un variant = Null, pCallback est une procédure) : Null // Criar uma thread para executar a requisição assíncrona ThreadExécute("AsyncRequestThread", threadNormal, this, nMethod, sEndpoint, vData, pCallback) RENVOYER Null FIN PROCÉDURE PUBLIQUE AsyncRequestThread(nMethod est un ERestMethod, sEndpoint est une chaîne, vData est un variant, pCallback est une procédure) // Executar a requisição LOCAL oResponse est un JSON = ExecuteRequest(nMethod, sEndpoint, vData) // Chamar o callback com o resultado SI pCallback..Nom <> "" ALORS pCallback(oResponse, _nResponseCode, _sLastErrorMessage) FIN FIN // Método para criar uma nova instância da classe (padrão factory) FONCTION PUBLIQUE STATIC CreateInstance(sBaseURL est une chaîne = "") : RestAPI RENVOYER (Nouveau RestAPI(sBaseURL)) FIN // Método especializado para lidar com APIs que implementam HAL (Hypertext Application Language) FONCTION PUBLIQUE FollowHALLink(oResponse est un JSON, sRelName est une chaîne) : JSON SI oResponse = Null OU PAS oResponse._links..Existe OU PAS oResponse._links[sRelName]..Existe ALORS _sLastErrorMessage = "Relação HAL não encontrada: " + sRelName RENVOYER Null FIN LOCAL sUrl est une chaîne SI TypeVar(oResponse._links[sRelName]) = typVariant ET oResponse._links[sRelName].href..Existe ALORS sUrl = oResponse._links[sRelName].href SINON SI TypeVar(oResponse._links[sRelName]) = typChaîne ALORS sUrl = oResponse._links[sRelName] SINON _sLastErrorMessage = "Formato de link HAL inválido para: " + sRelName RENVOYER Null FIN // Verificar se é URL absoluta ou relativa SI Gauche(sUrl, 4) = "http" OU Gauche(sUrl, 2) = "//" ALORS // URL absoluta, substituir a base URL LOCAL sOldBaseURL est une chaîne = _sBaseURL _sBaseURL = sUrl LOCAL oResult est un JSON = GET("") _sBaseURL = sOldBaseURL RENVOYER oResult SINON // URL relativa RENVOYER GET(sUrl) FIN FIN // Método para lidar com paginação em APIs REST FONCTION PUBLIQUE GetAllPaginatedResults(sEndpoint est une chaîne, sPageParam est une chaîne = "page", sSizeParam est une chaîne = "size", nPageSize est un entier = 100) : tableau de JSON LOCAL tabResults est un tableau de JSON LOCAL nPage est un entier = 1 LOCAL bHasMore est un booléen = Vrai TANTQUE bHasMore // Limpar parâmetros existentes de paginação RemoveQueryParam(sPageParam) RemoveQueryParam(sSizeParam) // Adicionar parâmetros de paginação AddQueryParam(sPageParam, NumériqueVersChaine(nPage)) AddQueryParam(sSizeParam, NumériqueVersChaine(nPageSize)) // Executar a requisição LOCAL oResponse est un JSON = GET(sEndpoint) // Verificar se obteve resultados SI oResponse = Null ALORS LogDebug("Erro ao obter página " + NumériqueVersChaine(nPage) + ": " + _sLastErrorMessage) SORTIR FIN // Adicionar à lista de resultados TableauAjoute(tabResults, oResponse) // Verificar se há mais páginas // O critério pode variar dependendo da API SI oResponse.page..Existe ET oResponse.totalPages..Existe ALORS // Formato Spring/padrão bHasMore = (oResponse.page < oResponse.totalPages) SINON SI oResponse.meta..Existe ET oResponse.meta.pagination..Existe ALORS // Formato com meta.pagination bHasMore = (oResponse.meta.pagination.current_page < oResponse.meta.pagination.total_pages) SINON SI oResponse.next..Existe ET oResponse.next <> Null ALORS // Formato com next/previous bHasMore = Vrai SINON SI TableauOccurrence(oResponse) < nPageSize ALORS // Formato simples (array de resultados) bHasMore = Faux SINON // Assumir que não há mais páginas se não puder determinar bHasMore = Faux FIN nPage++ FIN RENVOYER tabResults FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 1:50 AM
// ------------------------------------------------------------------------------------------------ // Exemplos de Uso da Classe RestAPI v5.0 // ------------------------------------------------------------------------------------------------ // Este código demonstra exemplos práticos de uso da classe RestAPI com diferentes tipos de autenticação // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ // Exemplo 1: Autenticação Básica (EAuthType.BASIC) // -------------------------------------------------------------------------------- PROCEDURE ExemploAutenticacaoBasica() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v1 ") // Configurar autenticação básica api.SetAuthBasic("usuario", "senha") // Configurar timeout mais longo (60 segundos) api.SetTimeout(60) // Ativar modo debug api.SetDebugMode(Vrai, fRepExe() + [fSep] + "logs") // Fazer requisição GET resultat est un JSON = api.GET("usuarios") // Verificar se a requisição foi bem-sucedida SI api.IsSuccess() ALORS // Processar os resultados POUR TOUT usuario DE resultat Trace("Usuário: " + usuario.nome + ", Email: " + usuario.email) FIN SINON // Exibir mensagem de erro Erreur("Falha na requisição: " + api.GetLastError()) FIN // Fazer requisição POST para criar um novo usuário novoUsuario est un JSON novoUsuario.nome = "João Silva" novoUsuario.email = "joao.silva@exemplo.com" novoUsuario.cargo = "Desenvolvedor" resultado est un JSON = api.POST("usuarios", novoUsuario) SI resultado <> Null ALORS Info("Usuário criado com sucesso! ID: " + resultado.id) FIN FIN // Exemplo 2: Autenticação com Token JWT (EAuthType.BEARER) // -------------------------------------------------------------------------------- PROCEDURE ExemploAutenticacaoBearer() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v2 ") // Primeiro, obter o token de autenticação reqLogin est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/auth ") reqLogin.SetContentType(EContentType.JSON) dadosLogin est un JSON dadosLogin.username = "admin" dadosLogin.password = "senha123" resposta est un JSON = reqLogin.POST("login", dadosLogin) SI resposta = Null OU resposta.token = Null ALORS Erreur("Falha na autenticação: " + reqLogin.GetLastError()) RETOUR FIN // Configurar autenticação Bearer com o token obtido api.SetAuthBearer(resposta.token) // Configurar headers adicionais api.AddHeader("X-App-Version", "5.0.0") // Fazer requisição para endpoint protegido dadosProtegidos est un JSON = api.GET("dados/protegidos") SI dadosProtegidos <> Null ALORS // Processar dados protegidos TableauInfo(dadosProtegidos, "Dados protegidos") SINON Erreur("Falha ao acessar dados protegidos: " + api.GetLastError()) FIN FIN // Exemplo 3: Autenticação com API Key (EAuthType.API_KEY) // -------------------------------------------------------------------------------- PROCEDURE ExemploAutenticacaoApiKey() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v3 ") // Configurar autenticação com API Key no header api.SetAuthApiKey("abc123xyz456", "X-API-Key", Vrai) // Desativar verificação SSL (apenas para ambiente de desenvolvimento!) api.SetSSLVerification(Faux) // Configurar retentativas automáticas api.SetAutoRetry(Vrai, 3, 2000) // 3 tentativas com 2 segundos de intervalo // Buscar lista de produtos com parâmetros api.AddQueryParam("categoria", "eletronicos") api.AddQueryParam("ordenar", "preco") api.AddQueryParam("direcao", "asc") produtos est un JSON = api.GET("produtos") SI produtos <> Null ALORS // Processar a lista de produtos Trace("Total de produtos: " + NumériqueVersChaine(TableauOccurrence(produtos))) // Exibir preço médio somaPrecos est un réel = 0 POUR TOUT produto DE produtos somaPrecos += produto.preco FIN precoMedio est un réel = somaPrecos / TableauOccurrence(produtos) Trace("Preço médio: " + NumériqueVersChaine(precoMedio, "F2")) FIN // Upload de uma imagem de produto imagemId est un JSON = api.UploadFile("produtos/1/imagem", fRepExe() + [fSep] + "imagens" + [fSep] + "produto1.jpg", "imagem") SI imagemId <> Null ALORS Info("Imagem enviada com sucesso! ID: " + imagemId.id) FIN FIN // Exemplo 4: Autenticação OAuth 2.0 (EAuthType.OAUTH2) // -------------------------------------------------------------------------------- PROCEDURE ExemploAutenticacaoOAuth2() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v4 ") // Configurar autenticação OAuth 2.0 api.SetAuthOAuth2( "cliente_id_12345", // Client ID "cliente_secreto_67890", // Client Secret "https://auth.exemplo.com/oauth/token ", // Token Endpoint "read write delete" // Escopo de acesso ) // Obter token OAuth 2.0 SI api.RefreshOAuth2Token() = Faux ALORS Erreur("Falha ao obter token OAuth 2.0: " + api.GetLastError()) RETOUR FIN // Configurar Content-Type api.SetContentType(EContentType.JSON) // Executar operações assíncronas api.ExecuteAsync(ERestMethod.GET, "estatisticas", Null, CallbackEstatisticas) // Continuar executando outras operações enquanto a requisição assíncrona é processada Info("Requisição assíncrona iniciada. Continue trabalhando enquanto os dados são carregados...") // Exemplo de requisição síncrona enquanto isso perfil est un JSON = api.GET("meu-perfil") SI perfil <> Null ALORS Info("Perfil: " + perfil.nome) FIN FIN // Callback para requisição assíncrona PROCEDURE CallbackEstatisticas(resultat est un JSON, nCode est un entier, sError est une chaîne) SI resultat <> Null ALORS Info("Estatísticas recebidas: " + VariantVersJSON(resultat)) SINON Erreur("Falha ao obter estatísticas: " + sError + " (código: " + NumériqueVersChaine(nCode) + ")") FIN FIN // Exemplo 5: Autenticação Digest (EAuthType.DIGEST) // -------------------------------------------------------------------------------- PROCEDURE ExemploAutenticacaoDigest() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v5 ") // Configurar autenticação Digest api.SetAuthDigest("usuario_digest", "senha_digest") // Configurar User-Agent personalizado api.SetUserAgent("MinhaApp/2.0 (WinDev; RestAPI v5.0)") // Fazer requisição para endpoint protegido com digest dadosUsuario est un JSON = api.GET("perfil") SI dadosUsuario <> Null ALORS // Exibir informações do usuário Info("Bem-vindo, " + dadosUsuario.nome + "!") Info("Último acesso: " + dadosUsuario.ultimo_acesso) SINON Erreur("Falha ao acessar perfil: " + api.GetLastError()) FIN FIN // Exemplo 6: Autenticação HMAC (EAuthType.HMAC) // -------------------------------------------------------------------------------- PROCEDURE ExemploAutenticacaoHMAC() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v6 ") // Configurar autenticação HMAC com SHA-256 api.SetAuthHMAC("chave_secreta_para_hmac_123456789", "SHA256") // Configurar cabeçalhos adicionais api.AddHeader("X-App-ID", "minha-aplicacao") // Fazer uma requisição PUT para atualizar dados dadosAtualizacao est un JSON dadosAtualizacao.id = 123 dadosAtualizacao.status = "ativo" dadosAtualizacao.ultima_atualizacao = DateHeureVersChaîne(DateHeureSys()) resultado est un JSON = api.PUT("recursos/123", dadosAtualizacao) SI resultado <> Null ALORS Info("Recurso atualizado com sucesso!") SINON Erreur("Falha ao atualizar recurso: " + api.GetLastError()) FIN // Fazer uma requisição DELETE confirmacao est un JSON = api.DELETE("recursos/456") SI confirmacao <> Null ET confirmacao.sucesso = Vrai ALORS Info("Recurso excluído com sucesso!") SINON Erreur("Falha ao excluir recurso: " + api.GetLastError()) FIN FIN // Exemplo 7: Autenticação Personalizada (EAuthType.CUSTOM) // -------------------------------------------------------------------------------- PROCEDURE ExemploAutenticacaoCustom() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v7 ") // Gerar um timestamp para autenticação timestamp est une chaîne = NumériqueVersChaine(DateHeureVersEntier(DateHeureSys())) // Gerar um nonce aleatório nonce est une chaîne = ProcédureVersChaine(AleatoireEntre(100000, 999999)) // Chave de autenticação chave est une chaîne = "chave_secreta_123" // Construir assinatura personalizada stringToSign est une chaîne = "GET:/recurso:" + timestamp + ":" + nonce assinatura est une chaîne = Encode(HashChaîne(cryptAlgoSHA256, stringToSign, chave), encodeBASE64) // Montar valor de autenticação personalizada valorAuth est une chaîne = "Custom ts=" + timestamp + ",nonce=" + nonce + ",signature=" + assinatura // Configurar autenticação personalizada api.SetCustomAuth(valorAuth) // Fazer requisição para endpoint protegido recursos est un JSON = api.GET("recurso") SI recursos <> Null ALORS // Processar recursos POUR TOUT recurso DE recursos Trace("Recurso: " + recurso.nome) FIN SINON Erreur("Falha ao acessar recursos: " + api.GetLastError()) FIN FIN // Exemplo 8: Manipulação de Paginação e HAL // -------------------------------------------------------------------------------- PROCEDURE ExemploPaginacaoEHAL() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v8 ") // Configurar autenticação Bearer api.SetAuthBearer("token_de_acesso_jwt_123456789") // Obter todos os resultados paginados todosResultados est un tableau de JSON = api.GetAllPaginatedResults("clientes", "pagina", "tamanho", 50) // Processar todos os resultados totalClientes est un entier = 0 POUR TOUT pagina DE todosResultados SI pagina._embedded..Existe ET pagina._embedded.clientes..Existe ALORS // API no formato HAL POUR TOUT cliente DE pagina._embedded.clientes totalClientes++ Trace(NumériqueVersChaine(totalClientes) + ". " + cliente.nome) FIN SINON // Formato padrão POUR TOUT cliente DE pagina totalClientes++ Trace(NumériqueVersChaine(totalClientes) + ". " + cliente.nome) FIN FIN FIN Info("Total de clientes processados: " + NumériqueVersChaine(totalClientes)) // Exemplo de requisição com HAL e navegação por links primeiraResposta est un JSON = api.GET("pedidos/recentes") SI primeiraResposta <> Null ET primeiraResposta._links..Existe ALORS // Seguir um link HAL proximaResposta est un JSON = api.FollowHALLink(primeiraResposta, "next") SI proximaResposta <> Null ALORS Info("Próxima página obtida com sucesso!") FIN // Seguir um link para detalhes de um item específico SI primeiraResposta._embedded..Existe ET primeiraResposta._embedded.pedidos..Existe ET TableauOccurrence(primeiraResposta._embedded.pedidos) > 0 ALORS primeiroPedido est un variant = primeiraResposta._embedded.pedidos[1] SI primeiroPedido._links..Existe ET primeiroPedido._links.self..Existe ALORS // Obter detalhes do pedido usando o link HAL detalhes est un JSON = api.FollowHALLink(primeiroPedido, "self") SI detalhes <> Null ALORS Info("Detalhes do pedido #" + detalhes.numero + " obtidos com sucesso!") FIN FIN FIN FIN FIN // Exemplo 9: Download e Upload de Arquivos // -------------------------------------------------------------------------------- PROCEDURE ExemploArquivos() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v9 ") // Configurar autenticação API Key api.SetAuthApiKey("chave_api_para_arquivos_123", "X-API-Key") // Download de um arquivo destino est une chaîne = fRepDonnées() + [fSep] + "downloads" + [fSep] + "relatorio.pdf" // Verificar se o diretório existe, senão, criar SI PAS fRepertoireExiste(fExtraitChemin(destino, fDisque + fRépertoire)) ALORS fRepCrée(fExtraitChemin(destino, fDisque + fRépertoire)) FIN // Executar o download SI api.DownloadFile("relatorios/mensal", destino) ALORS Info("Arquivo baixado com sucesso para: " + destino) // Abrir o arquivo LanceAppliAssociée(destino) SINON Erreur("Falha ao baixar arquivo: " + api.GetLastError()) FIN // Upload de múltiplos arquivos localDir est une chaîne = fRepDonnées() + [fSep] + "uploads" listaArquivos est un tableau de chaînes = fListeFichier(localDir + [fSep] + "*.jpg", frNormal) POUR TOUT arquivo DE listaArquivos caminhoCompleto est une chaîne = localDir + [fSep] + arquivo // Upload do arquivo resultado est un JSON = api.UploadFile("imagens/upload", caminhoCompleto) SI resultado <> Null ALORS Info("Arquivo " + arquivo + " enviado com sucesso. ID: " + resultado.id) SINON Erreur("Falha ao enviar arquivo " + arquivo + ": " + api.GetLastError()) FIN FIN FIN // Exemplo 10: Uso Avançado com Tratamento de Erros // -------------------------------------------------------------------------------- PROCEDURE ExemploAvancado() // Criar instância da API api est un RestAPI = RestAPI.CreateInstance("https://api.exemplo.com/v10 ") // Configurar Bearer Token api.SetAuthBearer("token_de_acesso_avancado_123456") // Ativar modo debug e retentativas api.SetDebugMode(Vrai) api.SetAutoRetry(Vrai, 2, 1500) // Lista para armazenar operações bem-sucedidas e falhas operacoesSucesso est un tableau de chaînes operacoesFalha est un tableau associatif de chaînes // Lista de IDs para processar listaIDs est un tableau d'entiers = [101, 102, 103, 104, 105] POUR TOUT id DE listaIDs // Tentar obter dados para cada ID dados est un JSON = api.GET("entidades/" + NumériqueVersChaine(id)) SI dados <> Null ALORS // Operação bem-sucedida TableauAjoute(operacoesSucesso, "ID " + NumériqueVersChaine(id) + ": " + données.nome) // Verificar se é necessário atualização SI données.necessita_atualizacao = Vrai ALORS // Preparar dados para atualização atualizacao est un JSON atualizacao.id = id atualizacao.status = "atualizado" atualizacao.data_atualizacao = DateHeureVersChaîne(DateHeureSys()) // Executar atualização resultado est un JSON = api.PUT("entidades/" + NumériqueVersChaine(id), atualizacao) SI resultado <> Null ALORS TableauAjoute(operacoesSucesso, "Atualização ID " + NumériqueVersChaine(id) + " concluída") SINON operacoesFalha["Atualização ID " + NumériqueVersChaine(id)] = api.GetLastError() FIN FIN SINON // Operação falhou operacoesFalha["ID " + NumériqueVersChaine(id)] = api.GetLastError() // Verificar código de erro SI api.GetLastResponseCode() = 404 ALORS // Entidade não encontrada, talvez criar uma nova? Trace("Entidade ID " + NumériqueVersChaine(id) + " não encontrada. Considerar criação.") FIN FIN FIN // Exibir relatório de operações Info("Operações concluídas com sucesso: " + NumériqueVersChaine(TableauOccurrence(operacoesSucesso))) Info("Operações com falha: " + NumériqueVersChaine(TableauOccurrence(operacoesFalha))) // Registrar resultados em log SI TableauOccurrence(operacoesFalha) > 0 ALORS Trace("Detalhes das falhas:") POUR TOUT pFalha DE operacoesFalha Trace("- " + pFalha..Nom + ": " + pFalha) FIN FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:27 AM
// ------------------------------------------------------------------------------------------------ // RestEnums.wdc - Centralized Enumerations for RestAPI v6.0 // ------------------------------------------------------------------------------------------------ // All enumerations used across the RestAPI system // Author: Claude // Creation Date: 04/03/2025 // Version: 6.0 // ------------------------------------------------------------------------------------------------ // HTTP methods supported by the API ENUMERATION ERestMethod GET = 1 // Retrieve resources POST = 2 // Create new resources PUT = 3 // Update existing resources (complete replacement) DELETE = 4 // Remove resources PATCH = 5 // Partial update of resources HEAD = 6 // Get headers only (no body) OPTIONS = 7 // Get supported methods/options TRACE = 8 // Diagnostic method (echo request) FIN // Authentication types supported by the API ENUMERATION EAuthType NONE = 0 // No authentication BASIC = 1 // Basic authentication (username:password) BEARER = 2 // Bearer token authentication (JWT) API_KEY = 3 // API Key authentication OAUTH2 = 4 // OAuth 2.0 authentication DIGEST = 5 // Digest authentication HMAC = 6 // HMAC signature authentication NTLM = 7 // Windows NTLM authentication KERBEROS = 8 // Kerberos authentication AWS_SIGNATURE = 9 // AWS Signature v4 CUSTOM = 10 // Custom authentication FIN // Content types supported by the API ENUMERATION EContentType JSON = 1 // application/json XML = 2 // application/xml FORM = 3 // application/x-www-form-urlencoded MULTIPART = 4 // multipart/form-data TEXT = 5 // text/plain HTML = 6 // text/html CSV = 7 // text/csv BINARY = 8 // application/octet-stream PDF = 9 // application/pdf CUSTOM = 10 // Custom content type FIN // Log levels for the Logger component ENUMERATION ELogLevel TRACE = 1 // Most detailed information DEBUG = 2 // Detailed information for debugging INFO = 3 // General information WARNING = 4 // Warnings ERROR = 5 // Errors CRITICAL = 6 // Critical errors NONE = 7 // No logging FIN // Error handling strategies ENUMERATION EErrorStrategy THROW = 1 // Throw an exception RETURN_NULL = 2 // Return null silently RETRY = 3 // Retry the operation CALLBACK = 4 // Call an error handler LOG_ONLY = 5 // Just log the error IGNORE = 6 // Completely ignore the error FIN // Cache strategies ENUMERATION ECacheStrategy NONE = 0 // No caching MEMORY = 1 // In-memory caching FILE = 2 // File-based caching MEMORY_THEN_FILE = 3 // Memory with file backup HYBRID = 4 // Custom hybrid approach FIN // OAuth 2.0 grant types ENUMERATION EOAuth2GrantType CLIENT_CREDENTIALS = 1 // Client credentials grant AUTHORIZATION_CODE = 2 // Authorization code grant PASSWORD = 3 // Resource owner password credentials IMPLICIT = 4 // Implicit grant (legacy) REFRESH_TOKEN = 5 // Refresh token grant DEVICE_CODE = 6 // Device code grant FIN // Pagination types for REST APIs ENUMERATION EPaginationType NONE = 0 // No pagination OFFSET = 1 // Offset-based pagination (skip/limit) PAGE = 2 // Page-based pagination (page/size) CURSOR = 3 // Cursor-based pagination (next/prev tokens) LINK_HEADER = 4 // Link header pagination (RFC 5988) HAL = 5 // HAL-style pagination (_links) CUSTOM = 6 // Custom pagination strategy FIN // Rate limiting strategies ENUMERATION ERateLimitStrategy NONE = 0 // No rate limiting FIXED_WINDOW = 1 // Fixed window counter SLIDING_WINDOW = 2 // Sliding window counter TOKEN_BUCKET = 3 // Token bucket algorithm LEAKY_BUCKET = 4 // Leaky bucket algorithm ADAPTIVE = 5 // Adaptive rate limiting FIN // API response formats ENUMERATION EResponseFormat AUTO = 0 // Auto-detect from Content-Type JSON = 1 // JSON response XML = 2 // XML response TEXT = 3 // Plain text response BINARY = 4 // Binary response CUSTOM = 5 // Custom response format FIN // HTTP status code categories ENUMERATION EStatusCategory INFORMATIONAL = 1 // 1xx status codes SUCCESS = 2 // 2xx status codes REDIRECTION = 3 // 3xx status codes CLIENT_ERROR = 4 // 4xx status codes SERVER_ERROR = 5 // 5xx status codes UNKNOWN = 6 // Unknown or non-standard codes FIN // Parameter locations ENUMERATION EParamLocation PATH = 1 // URL path parameter QUERY = 2 // Query string parameter HEADER = 3 // HTTP header BODY = 4 // Request body FORM = 5 // Form parameter COOKIE = 6 // Cookie parameter FIN // Authentication token types ENUMERATION ETokenType JWT = 1 // JSON Web Token OPAQUE = 2 // Opaque token MAC = 3 // Message Authentication Code token SAML = 4 // SAML token OAUTH1 = 5 // OAuth 1.0 token OAUTH2 = 6 // OAuth 2.0 token CUSTOM = 7 // Custom token format FIN // Connection security levels ENUMERATION ESecurityLevel NONE = 0 // No security (HTTP) SSL = 1 // SSL/TLS (HTTPS) SSL_PINNED = 2 // SSL with certificate pinning MTLS = 3 // Mutual TLS (client certificates) E2E = 4 // End-to-end encryption FIN // Retry strategies for failed requests ENUMERATION ERetryStrategy NONE = 0 // No retry FIXED = 1 // Fixed interval retry EXPONENTIAL = 2 // Exponential backoff LINEAR = 3 // Linear backoff RANDOM = 4 // Random jitter backoff CUSTOM = 5 // Custom retry strategy FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:29 AM
# RestAPI v6.0 Project Structure ## Overview RestAPI v6.0 is a comprehensive object-oriented solution for consuming REST APIs in WinDev applications. It follows modern OO design principles, making extensive use of inheritance, polymorphism, and encapsulation to provide a flexible and extensible framework. ## Project Components ### Core 1. **RestEnums.wdc** - Contains all enumerations used across the system - Centralized location for type definitions 2. **RestCore.wdc** - Base abstract class for all components - Contains common functionality and interfaces 3. **RestConfig.wdc** - Configuration management - Default settings and global options ### Authentication 4. **RestAuth.wdc** - Abstract base class for all authentication methods - Defines the authentication interface 5. **RestAuthBasic.wdc** - Implements HTTP Basic authentication 6. **RestAuthBearer.wdc** - Implements Bearer token authentication (JWT) 7. **RestAuthApiKey.wdc** - Implements API Key authentication 8. **RestAuthOAuth2.wdc** - Implements OAuth 2.0 authentication - Supports different grant types 9. **RestAuthDigest.wdc** - Implements HTTP Digest authentication 10. **RestAuthHMAC.wdc** - Implements HMAC signature authentication 11. **RestAuthCustom.wdc** - Implements custom authentication methods - Supports callback functions for extensibility ### HTTP 12. **RestRequest.wdc** - Encapsulates an HTTP request - Handles headers, parameters, and payload formatting 13. **RestResponse.wdc** - Encapsulates an HTTP response - Parses different content types - Provides typed accessors 14. **RestEndpoint.wdc** - Represents a specific API endpoint - Has its own settings and behaviors ### Utilities 15. **RestLogger.wdc** - Handles logging of requests, responses, and errors - Multiple output destinations (file, console, etc) 16. **RestCache.wdc** - Caches responses to improve performance - Configurable caching strategies 17. **RestUtils.wdc** - Utility functions used across the system - String manipulation, encoding, etc. 18. **RestFileTransfer.wdc** - Specialized class for file uploads and downloads ### Advanced Features 19. **RestPagination.wdc** - Handles different pagination strategies - Automatic page traversal 20. **RestRateLimit.wdc** - Manages API rate limits - Implements throttling and backoff strategies 21. **RestHAL.wdc** - Support for Hypertext Application Language - Link traversal and resource discovery 22. **RestWebSocket.wdc** - WebSocket support for real-time APIs ### Main Interfaces 23. **RestAPI.wdc** - Main entry point for the library - Facade that coordinates all components 24. **RestCollection.wdc** - Manages collections of API endpoints - Support for CRUD operations ## Class Relationships ``` RestCore ├─ RestAPI ├─ RestEndpoint ├─ RestRequest ├─ RestResponse ├─ RestAuth │ ├─ RestAuthBasic │ ├─ RestAuthBearer │ ├─ RestAuthApiKey │ ├─ RestAuthOAuth2 │ ├─ RestAuthDigest │ ├─ RestAuthHMAC │ └─ RestAuthCustom ├─ RestLogger ├─ RestCache ├─ RestPagination ├─ RestRateLimit ├─ RestHAL └─ RestFileTransfer ``` ## Examples The project includes comprehensive examples for each authentication type: - Basic Auth Example - Bearer Token Example - API Key Example - OAuth 2.0 Example (all grant types) - Digest Auth Example - HMAC Signature Example - Custom Auth Example Additionally, examples for advanced features: - File Upload/Download - Pagination - Rate Limiting - HAL Traversal - WebSocket Communication ## Documentation Full documentation is available in both English and Portuguese, including: - Architecture Overview - Class Reference - Integration Guide - Authentication Examples - Advanced Usage Scenarios -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:30 AM
// ------------------------------------------------------------------------------------------------ // RestRequest.wdc - Classe de Requisição // ------------------------------------------------------------------------------------------------ // Encapsula uma requisição HTTP // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestRequest // Propriedades PRIVE _nMethod est un ERestMethod = ERestMethod.GET _sURL est une chaîne _tabHeaders est un tableau associatif de chaînes _tabQueryParams est un tableau associatif de chaînes _nContentType est un EContentType = EContentType.JSON _vData est un variant _sFormattedData est une chaîne _nTimeout est un entier = 30 _bSSLVerification est un booléen = Vrai _auth est un RestAuth = Null _logger est un RestLogger = Null // Construtores PROCEDURE PUBLIQUE Constructeur() // Inicializar cabeçalhos padrão InitHeaders() FIN PROCEDURE PUBLIQUE Constructeur(sURL est une chaîne) _sURL = sURL InitHeaders() FIN PROCEDURE PUBLIQUE Constructeur(sURL est une chaîne, nMethod est un ERestMethod) _sURL = sURL _nMethod = nMethod InitHeaders() FIN PROCEDURE PUBLIQUE Constructeur(sURL est une chaîne, nMethod est un ERestMethod, nContentType est un EContentType) _sURL = sURL _nMethod = nMethod _nContentType = nContentType InitHeaders() FIN // Métodos de configuração geral PROCEDURE PUBLIQUE SetURL(sURL est une chaîne) _sURL = sURL FIN PROCEDURE PUBLIQUE SetMethod(nMethod est un ERestMethod) _nMethod = nMethod FIN PROCEDURE PUBLIQUE SetContentType(nContentType est un EContentType) _nContentType = nContentType _tabHeaders["Content-Type"] = RestUtils.ContentTypeToString(nContentType) FIN PROCEDURE PUBLIQUE SetTimeout(nSeconds est un entier) _nTimeout = nSeconds FIN PROCEDURE PUBLIQUE SetSSLVerification(bVerify est un booléen) _bSSLVerification = bVerify FIN PROCEDURE PUBLIQUE SetLogger(logger est un RestLogger) _logger = logger FIN PROCEDURE PUBLIQUE SetAuth(auth est un RestAuth) _auth = auth SI _auth <> Null ET _logger <> Null ALORS _auth.SetLogger(_logger) FIN FIN // Configuração de dados PROCEDURE PUBLIQUE SetData(vData est un variant) _vData = vData // Formatação é feita apenas na execução para garantir que o ContentType atual seja usado FIN PROCEDURE PUBLIQUE SetRawData(sData est une chaîne) _sFormattedData = sData FIN // Configuração de cabeçalhos PROCEDURE PUBLIQUE AddHeader(sName est une chaîne, sValue est une chaîne) _tabHeaders[sName] = sValue FIN PROCEDURE PUBLIQUE RemoveHeader(sName est une chaîne) SI _tabHeaders[sName]..Existe ALORS TableauSupprime(_tabHeaders, sName) FIN FIN PROCEDURE PUBLIQUE ClearHeaders() TableauSupprimeTout(_tabHeaders) InitHeaders() FIN // Configuração de parâmetros de consulta PROCEDURE PUBLIQUE AddQueryParam(sName est une chaîne, sValue est une chaîne) _tabQueryParams[sName] = sValue FIN PROCEDURE PUBLIQUE RemoveQueryParam(sName est une chaîne) SI _tabQueryParams[sName]..Existe ALORS TableauSupprime(_tabQueryParams, sName) FIN FIN PROCEDURE PUBLIQUE ClearQueryParams() TableauSupprimeTout(_tabQueryParams) FIN // Getters FONCTION PUBLIQUE GetURL() : chaîne RENVOYER _sURL FIN FONCTION PUBLIQUE GetMethod() : ERestMethod RENVOYER _nMethod FIN FONCTION PUBLIQUE GetContentType() : EContentType RENVOYER _nContentType FIN FONCTION PUBLIQUE GetData() : variant RENVOYER _vData FIN FONCTION PUBLIQUE GetFormattedData() : chaîne SI _sFormattedData = "" ET _vData <> Null ALORS _sFormattedData = RestUtils.FormatData(_vData, _nContentType) FIN RENVOYER _sFormattedData FIN FONCTION PUBLIQUE GetHeaders() : tableau associatif de chaînes RENVOYER _tabHeaders FIN FONCTION PUBLIQUE GetQueryParams() : tableau associatif de chaînes RENVOYER _tabQueryParams FIN FONCTION PUBLIQUE GetTimeout() : entier RENVOYER _nTimeout FIN FONCTION PUBLIQUE GetSSLVerification() : booléen RENVOYER _bSSLVerification FIN // Construção da URL completa com query params FONCTION PUBLIQUE GetFullURL() : chaîne sFullURL est une chaîne = _sURL // Adicionar query params SI TableauOccurrence(_tabQueryParams) > 0 ALORS sFullURL += RestUtils.BuildQueryString(_tabQueryParams) FIN RENVOYER sFullURL FIN // Método para aplicar autenticação FONCTION PUBLIQUE ApplyAuth() : booléen SI _auth = Null ALORS RENVOYER Vrai // Sem autenticação, retornar sucesso FIN // Verificar se a autenticação é válida SI PAS _auth.IsAuthValid() ALORS // Tentar atualizar a autenticação, se possível SI _auth.RefreshAuth() = Faux ALORS SI _logger <> Null ALORS _logger.Error("Falha ao atualizar autenticação") FIN RENVOYER Faux FIN FIN // Aplicar autenticação sMethodStr est une chaîne = RestUtils.MethodToString(_nMethod) sDataStr est une chaîne = GetFormattedData() RENVOYER _auth.ApplyAuth(_tabHeaders, _tabQueryParams, sMethodStr, _sURL, sDataStr) FIN // Inicialização de cabeçalhos padrão PRIVE PROCEDURE InitHeaders() _tabHeaders["User-Agent"] = "WinDev RestAPI Client v5.0" _tabHeaders["Accept"] = "application/json" _tabHeaders["Content-Type"] = RestUtils.ContentTypeToString(_nContentType) FIN // Método para log PRIVE PROCEDURE LogDebug(sMessage est une chaîne) SI _logger <> Null ALORS _logger.Debug(sMessage) FIN FIN PRIVE PROCEDURE LogError(sMessage est une chaîne) SI _logger <> Null ALORS _logger.Error(sMessage) FIN FIN // Converte objeto para string (para debug/log) FONCTION PUBLIQUE ToString() : chaîne sMethodStr est une chaîne = RestUtils.MethodToString(_nMethod) sContentTypeStr est une chaîne = RestUtils.ContentTypeToString(_nContentType) sResult est une chaîne = sMethodStr + " " + GetFullURL() + EOT sResult += "Content-Type: " + sContentTypeStr + EOT // Adicionar outros cabeçalhos POUR TOUT pHeader DE _tabHeaders SI pHeader..Nom <> "Content-Type" ALORS sResult += pHeader..Nom + ": " + pHeader + EOT FIN FIN // Adicionar dados, se houver SI _vData <> Null OU _sFormattedData <> "" ALORS sResult += EOT + GetFormattedData() FIN RENVOYER sResult FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:31 AM
// ------------------------------------------------------------------------------------------------ // RestAuthDigest.wdc - Autenticação Digest // ------------------------------------------------------------------------------------------------ // Implementa autenticação HTTP Digest // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestAuthDigest HÉRITE DE RestAuth // Propriedades específicas PRIVE _sUsername est une chaîne _sPassword est une chaîne _sRealm est une chaîne = "" _sNonce est une chaîne = "" _sOpaque est une chaîne = "" _sQop est une chaîne = "auth" _sAlgorithm est une chaîne = "MD5" _sPath est une chaîne = "/" _sNonceCount est une chaîne = "00000001" _bReadyForAuthRequest est un booléen = Faux _sLastResponse est une chaîne = "" _nLastResponseCode est un entier = 0 _tabChallengeParams est un tableau associatif de chaînes _bHasStoredChallenge est un booléen = Faux // Construtores PROCEDURE PUBLIQUE Constructeur() Constructeur RestAuth(EAuthType.DIGEST) _sUsername = "" _sPassword = "" FIN PROCEDURE PUBLIQUE Constructeur(sUsername est une chaîne, sPassword est une chaîne) Constructeur RestAuth(EAuthType.DIGEST) _sUsername = sUsername _sPassword = sPassword FIN PROCEDURE PUBLIQUE Constructeur(sUsername est une chaîne, sPassword est une chaîne, logger est un RestLogger) Constructeur RestAuth(EAuthType.DIGEST, logger) _sUsername = sUsername _sPassword = sPassword FIN // Métodos públicos de configuração PROCEDURE PUBLIQUE SetCredentials(sUsername est une chaîne, sPassword est une chaîne) _sUsername = sUsername _sPassword = sPassword FIN FONCTION PUBLIQUE GetUsername() : chaîne RENVOYER _sUsername FIN // Métodos para configurar parâmetros de digest manualmente (se conhecidos de antemão) PROCEDURE PUBLIQUE SetDigestParams(sRealm est une chaîne, sNonce est une chaîne, sQop est une chaîne, sOpaque est une chaîne = "", sAlgorithm est une chaîne = "MD5") _sRealm = sRealm _sNonce = sNonce _sQop = sQop _sOpaque = sOpaque _sAlgorithm = sAlgorithm _bHasStoredChallenge = Vrai _bReadyForAuthRequest = Vrai FIN // Processar cabeçalho WWW-Authenticate da resposta 401 FONCTION PUBLIQUE ProcessAuthenticateHeader(sAuthHeader est une chaîne) : booléen // Verificar se o cabeçalho começa com "Digest " SI Gauche(sAuthHeader, 7) <> "Digest " ALORS LogError("Cabeçalho WWW-Authenticate não é do tipo Digest") RENVOYER Faux FIN // Extrair os parâmetros do cabeçalho sParams est une chaîne = Milieu(sAuthHeader, // Limpar parâmetros anteriores TableauSupprimeTout(_tabChallengeParams) // Parse dos parâmetros do digest tabParts est un tableau de chaînes = ExtraitChaîne(sParams, ",", 1, 10) POUR TOUT sPart DE tabParts // Remover espaços em branco sPart = Remplace(sPart, " ", "") // Extrair nome e valor do parâmetro (formato nome=valor) nPos est un entier = Position(sPart, "=") SI nPos > 0 ALORS sName est une chaîne = Gauche(sPart, nPos - 1) sValue est une chaîne = Milieu(sPart, nPos + 1) // Remover aspas, se presentes SI Gauche(sValue, 1) = """" ET Droite(sValue, 1) = """" ALORS sValue = Milieu(sValue, 2, Taille(sValue) - 2) FIN // Armazenar parâmetro _tabChallengeParams[sName] = sValue FIN FIN // Verificar se recebemos os parâmetros necessários SI _tabChallengeParams["realm"]..Existe ET _tabChallengeParams["nonce"]..Existe ALORS // Atualizar propriedades _sRealm = _tabChallengeParams["realm"] _sNonce = _tabChallengeParams["nonce"] // Opcionais SI _tabChallengeParams["qop"]..Existe ALORS _sQop = _tabChallengeParams["qop"] FIN SI _tabChallengeParams["opaque"]..Existe ALORS _sOpaque = _tabChallengeParams["opaque"] FIN SI _tabChallengeParams["algorithm"]..Existe ALORS _sAlgorithm = _tabChallengeParams["algorithm"] FIN // Resetar contador de nonce _sNonceCount = "00000001" _bHasStoredChallenge = Vrai _bReadyForAuthRequest = Vrai LogDebug("Parâmetros Digest processados com sucesso. Realm: " + _sRealm + ", Nonce: " + _sNonce) RENVOYER Vrai SINON LogError("Parâmetros Digest incompletos no cabeçalho WWW-Authenticate") RENVOYER Faux FIN FIN // Processar a resposta HTTP para extrair o desafio digest FONCTION PUBLIQUE ProcessHttpResponse(nStatusCode est un entier, tabResponseHeaders est un tableau associatif de chaînes, sResponseBody est une chaîne) : booléen // Armazenar a resposta para possível análise posterior _sLastResponse = sResponseBody _nLastResponseCode = nStatusCode // Se não for um código 401, não há desafio digest para processar SI nStatusCode <> 401 ALORS RENVOYER Faux FIN // Procurar pelo cabeçalho WWW-Authenticate SI tabResponseHeaders["WWW-Authenticate"]..Existe ALORS RENVOYER ProcessAuthenticateHeader(tabResponseHeaders["WWW-Authenticate"]) SINON LogError("Resposta 401 sem cabeçalho WWW-Authenticate") RENVOYER Faux FIN FIN // Sobrescrever métodos da classe base FONCTION PUBLIQUE ApplyAuth(tabHeaders est un tableau associatif de chaînes, tabQueryParams est un tableau associatif de chaînes, sMethod est une chaîne, sURL est une chaîne, sData est une chaîne) : booléen SI _sUsername = "" OU _sPassword = "" ALORS LogError("Credenciais digest não configuradas") RENVOYER Faux FIN // Se não tivermos os parâmetros de desafio digest, não podemos autenticar ainda SI PAS _bReadyForAuthRequest ALORS LogError("Parâmetros digest não recebidos do servidor. É necessário primeiro um desafio 401") RENVOYER Faux FIN // Extrair o path da URL para o digest _sPath = ExtractPathFromURL(sURL) // Gerar cabeçalho de autorização digest sAuthHeader est une chaîne = GenerateDigestHeader(sMethod) // Adicionar cabeçalho de autorização tabHeaders["Authorization"] = sAuthHeader // Incrementar contador de nonce para próxima requisição IncrementNonceCount() LogDebug("Adicionado cabeçalho de autenticação Digest") RENVOYER Vrai FIN FONCTION PUBLIQUE IsAuthValid() : booléen RENVOYER _sUsername <> "" ET _sPassword <> "" ET _bReadyForAuthRequest FIN PROCEDURE PUBLIQUE ClearAuth() _sUsername = "" _sPassword = "" _bReadyForAuthRequest = Faux _bHasStoredChallenge = Faux _sNonce = "" _sRealm = "" _sOpaque = "" FIN // Métodos privados PRIVE FONCTION GenerateDigestHeader(sMethod est une chaîne) : chaîne // Gerar um client nonce (cnonce) sCnonce est une chaîne = Gauche(Encode(RestUtils.GenerateNonce(), encodeBASE64), 16) // Calcular hashes para digest // HA1 = MD5(username:realm:password) sHA1Data est une chaîne = _sUsername + ":" + _sRealm + ":" + _sPassword sHA1 est une chaîne = Minuscule(HashVersHexa(sHA1Data, hachageMD5)) // HA2 = MD5(method:digestURI) sHA2Data est une chaîne = sMethod + ":" + _sPath sHA2 est une chaîne = Minuscule(HashVersHexa(sHA2Data, hachageMD5)) // Para qop="auth" sResponse est une chaîne SI _sQop = "auth" OU _sQop [= "auth" ALORS // response = MD5(HA1:nonce:nc:cnonce:qop:HA2) sResponseData est une chaîne = sHA1 + ":" + _sNonce + ":" + _sNonceCount + ":" + sCnonce + ":auth:" + sHA2 sResponse = Minuscule(HashVersHexa(sResponseData, hachageMD5)) SINON // response = MD5(HA1:nonce:HA2) para qop não especificado ou outros valores sResponseData est une chaîne = sHA1 + ":" + _sNonce + ":" + sHA2 sResponse = Minuscule(HashVersHexa(sResponseData, hachageMD5)) FIN // Construir cabeçalho de autorização sHeader est une chaîne = "Digest username=\"" + _sUsername + "\", " sHeader += "realm=\"" + _sRealm + "\", " sHeader += "nonce=\"" + _sNonce + "\", " sHeader += "uri=\"" + _sPath + "\", " SI _sQop <> "" ALORS // Para qop="auth" SI _sQop = "auth" OU _sQop [= "auth" ALORS sHeader += "qop=auth, " sHeader += "nc=" + _sNonceCount + ", " sHeader += "cnonce=\"" + sCnonce + "\", " FIN FIN sHeader += "response=\"" + sResponse + "\", " sHeader += "algorithm=\"" + _sAlgorithm + "\"" // Adicionar opaque se disponível SI _sOpaque <> "" ALORS sHeader += ", opaque=\"" + _sOpaque + "\"" FIN RENVOYER sHeader FIN PRIVE FONCTION ExtractPathFromURL(sURL est une chaîne) : chaîne // Remover protocolo (http:// ou https://) nStartPos est un entier SI Gauche(sURL, 7) = "http://" ALORS nStartPos = 8 SINON SI Gauche(sURL, = "https://" ALORS nStartPos = 9 SINON nStartPos = 1 FIN // Encontrar a primeira barra após o host sWithoutProtocol est une chaîne = Milieu(sURL, nStartPos) nSlashPos est un entier = Position(sWithoutProtocol, "/") // Se não houver barra, retornar "/" SI nSlashPos = 0 ALORS RENVOYER "/" FIN // Extrair o path (incluindo query string) sPath est une chaîne = Milieu(sWithoutProtocol, nSlashPos) // Se o path estiver vazio, retornar "/" SI sPath = "" ALORS RENVOYER "/" FIN RENVOYER sPath FIN PRIVE PROCEDURE IncrementNonceCount() // Converter para número, incrementar e formatar de volta como string com zeros à esquerda nCount est un entier = Val(_sNonceCount) nCount++ _sNonceCount = Complète(NumériqueVersChaine(nCount), "0", 8, complèteDroite) FIN // Método para converter bytes em hexadecimal PRIVE FONCTION HashVersHexa(sInput est une chaîne, nAlgo est un entier) : chaîne bufHash est un buffer = HashChaîne(nAlgo, sInput) SI bufHash = "" ALORS RENVOYER "" FIN sHexa est une chaîne = "" POUR i = 1 À Taille(bufHash) b est un entier = Asc(Milieu(bufHash, i, 1)) sHexa += NumériqueVersChaine(b, "x02") FIN RENVOYER sHexa FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:32 AM
// ------------------------------------------------------------------------------------------------ // RestAuthCustom.wdc - Autenticação Personalizada // ------------------------------------------------------------------------------------------------ // Implementa autenticação personalizada para APIs com requisitos específicos // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestAuthCustom HÉRITE DE RestAuth // Propriedades específicas PRIVE _sAuthHeaderName est une chaîne = "Authorization" _sAuthHeaderValue est une chaîne _tabCustomHeaders est un tableau associatif de chaînes _tabCustomQueryParams est un tableau associatif de chaînes _pCustomAuthFunction est une procédure = Null // Construtores PROCEDURE PUBLIQUE Constructeur() Constructeur RestAuth(EAuthType.CUSTOM) _sAuthHeaderValue = "" FIN PROCEDURE PUBLIQUE Constructeur(sAuthHeaderValue est une chaîne) Constructeur RestAuth(EAuthType.CUSTOM) _sAuthHeaderValue = sAuthHeaderValue FIN PROCEDURE PUBLIQUE Constructeur(sAuthHeaderName est une chaîne, sAuthHeaderValue est une chaîne) Constructeur RestAuth(EAuthType.CUSTOM) _sAuthHeaderName = sAuthHeaderName _sAuthHeaderValue = sAuthHeaderValue FIN PROCEDURE PUBLIQUE Constructeur(sAuthHeaderValue est une chaîne, logger est un RestLogger) Constructeur RestAuth(EAuthType.CUSTOM, logger) _sAuthHeaderValue = sAuthHeaderValue FIN // Métodos públicos de configuração PROCEDURE PUBLIQUE SetAuthHeader(sValue est une chaîne) _sAuthHeaderValue = sValue FIN PROCEDURE PUBLIQUE SetAuthHeaderName(sName est une chaîne) _sAuthHeaderName = sName FIN PROCEDURE PUBLIQUE AddCustomHeader(sName est une chaîne, sValue est une chaîne) _tabCustomHeaders[sName] = sValue FIN PROCEDURE PUBLIQUE RemoveCustomHeader(sName est une chaîne) SI _tabCustomHeaders[sName]..Existe ALORS TableauSupprime(_tabCustomHeaders, sName) FIN FIN PROCEDURE PUBLIQUE ClearCustomHeaders() TableauSupprimeTout(_tabCustomHeaders) FIN PROCEDURE PUBLIQUE AddCustomQueryParam(sName est une chaîne, sValue est une chaîne) _tabCustomQueryParams[sName] = sValue FIN PROCEDURE PUBLIQUE RemoveCustomQueryParam(sName est une chaîne) SI _tabCustomQueryParams[sName]..Existe ALORS TableauSupprime(_tabCustomQueryParams, sName) FIN FIN PROCEDURE PUBLIQUE ClearCustomQueryParams() TableauSupprimeTout(_tabCustomQueryParams) FIN // Definir uma função de autenticação personalizada PROCEDURE PUBLIQUE SetCustomAuthFunction(pFunction est une procédure) _pCustomAuthFunction = pFunction FIN // Obter valores atuais FONCTION PUBLIQUE GetAuthHeaderValue() : chaîne RENVOYER _sAuthHeaderValue FIN FONCTION PUBLIQUE GetAuthHeaderName() : chaîne RENVOYER _sAuthHeaderName FIN // Sobrescrever métodos da classe base FONCTION PUBLIQUE ApplyAuth(tabHeaders est un tableau associatif de chaînes, tabQueryParams est un tableau associatif de chaînes, sMethod est une chaîne, sURL est une chaîne, sData est une chaîne) : booléen // Se tiver uma função de autenticação personalizada, usá-la SI _pCustomAuthFunction <> Null ALORS // Chamar função personalizada _pCustomAuthFunction(_sAuthHeaderName, _sAuthHeaderValue, tabHeaders, tabQueryParams, sMethod, sURL, sData) LogDebug("Executada função de autenticação personalizada") RENVOYER Vrai SINON // Aplicar autenticação padrão SI _sAuthHeaderValue <> "" ALORS tabHeaders[_sAuthHeaderName] = _sAuthHeaderValue LogDebug("Adicionado cabeçalho de autenticação personalizada: " + _sAuthHeaderName) FIN // Adicionar cabeçalhos personalizados POUR TOUT pHeader DE _tabCustomHeaders tabHeaders[pHeader..Nom] = pHeader LogDebug("Adicionado cabeçalho personalizado: " + pHeader..Nom) FIN // Adicionar parâmetros de consulta personalizados POUR TOUT pParam DE _tabCustomQueryParams tabQueryParams[pParam..Nom] = pParam LogDebug("Adicionado parâmetro de consulta personalizado: " + pParam..Nom) FIN RENVOYER Vrai FIN FIN FONCTION PUBLIQUE IsAuthValid() : booléen RENVOYER _sAuthHeaderValue <> "" OU TableauOccurrence(_tabCustomHeaders) > 0 OU TableauOccurrence(_tabCustomQueryParams) > 0 OU _pCustomAuthFunction <> Null FIN PROCEDURE PUBLIQUE ClearAuth() _sAuthHeaderValue = "" TableauSupprimeTout(_tabCustomHeaders) TableauSupprimeTout(_tabCustomQueryParams) _pCustomAuthFunction = Null FIN // Métodos específicos para autenticação personalizada // Configuração para autenticação com assinatura personalizada PROCEDURE PUBLIQUE SetupSignatureAuth(sKeyID est une chaîne, sSignature est une chaîne, sAlgorithm est une chaîne = "") sValue est une chaîne = "Signature keyId=\"" + sKeyID + "\", signature=\"" + sSignature + "\"" SI sAlgorithm <> "" ALORS sValue += ", algorithm=\"" + sAlgorithm + "\"" FIN _sAuthHeaderValue = sValue FIN // Configuração para autenticação baseada em chave e timestamp PROCEDURE PUBLIQUE SetupKeyTimestampAuth(sKey est une chaîne, sHeaderPrefix est une chaîne = "Key") sTimestamp est une chaîne = NumériqueVersChaine(DateHeureVersEntier(DateHeureSys())) _sAuthHeaderValue = sHeaderPrefix + " " + sKey + ":" + sTimestamp FIN // Configuração para autenticação personalizada baseada em cabeçalhos específicos (não Authorization) PROCEDURE PUBLIQUE SetupMultiHeaderAuth(sApiKey est une chaîne, sKeyHeader est une chaîne, sAccessID est une chaîne = "", sAccessIDHeader est une chaîne = "") ClearCustomHeaders() // Configurar cabeçalho de API Key AddCustomHeader(sKeyHeader, sApiKey) // Configurar cabeçalho de Access ID, se fornecido SI sAccessID <> "" ET sAccessIDHeader <> "" ALORS AddCustomHeader(sAccessIDHeader, sAccessID) FIN // Configurar timestamp como cabeçalho separado, comum em APIs AddCustomHeader("X-Timestamp", RestUtils.GenerateTimestamp()) FIN // Método para gerar valor de autenticação com base em templates FONCTION PUBLIQUE GenerateAuthFromTemplate(sTemplate est une chaîne, tabValues est un tableau associatif de chaînes) : chaîne sAuthValue est une chaîne = sTemplate // Substituir cada placeholder por seu valor correspondente POUR TOUT pValue DE tabValues sAuthValue = Remplace(sAuthValue, "{" + pValue..Nom + "}", pValue) FIN _sAuthHeaderValue = sAuthValue RENVOYER sAuthValue FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:33 AM
// ------------------------------------------------------------------------------------------------ // RestAuthHMAC.wdc - Autenticação HMAC // ------------------------------------------------------------------------------------------------ // Implementa autenticação HMAC (Hash-based Message Authentication Code) // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestAuthHMAC HÉRITE DE RestAuth // Propriedades específicas PRIVE _sSecret est une chaîne _sAlgorithm est une chaîne = "SHA256" _sSignatureHeaderName est une chaîne = "X-Signature" _sTimestampHeaderName est une chaîne = "X-Timestamp" _sNonceHeaderName est une chaîne = "X-Nonce" _bIncludeMethod est un booléen = Vrai _bIncludeURL est un booléen = Vrai _bIncludeTimestamp est un booléen = Vrai _bIncludeNonce est un booléen = Vrai _bIncludeData est un booléen = Vrai _sCustomSignatureFormat est une chaîne = "" // Construtores PROCEDURE PUBLIQUE Constructeur() Constructeur RestAuth(EAuthType.HMAC) _sSecret = "" FIN PROCEDURE PUBLIQUE Constructeur(sSecret est une chaîne, sAlgorithm est une chaîne = "SHA256") Constructeur RestAuth(EAuthType.HMAC) _sSecret = sSecret _sAlgorithm = sAlgorithm FIN PROCEDURE PUBLIQUE Constructeur(sSecret est une chaîne, sAlgorithm est une chaîne, logger est un RestLogger) Constructeur RestAuth(EAuthType.HMAC, logger) _sSecret = sSecret _sAlgorithm = sAlgorithm FIN // Métodos públicos de configuração PROCEDURE PUBLIQUE SetSecret(sSecret est une chaîne) _sSecret = sSecret FIN PROCEDURE PUBLIQUE SetAlgorithm(sAlgorithm est une chaîne) _sAlgorithm = sAlgorithm FIN PROCEDURE PUBLIQUE SetHeaderNames(sSignatureHeader est une chaîne, sTimestampHeader est une chaîne, sNonceHeader est une chaîne) _sSignatureHeaderName = sSignatureHeader _sTimestampHeaderName = sTimestampHeader _sNonceHeaderName = sNonceHeader FIN PROCEDURE PUBLIQUE SetOptions(bIncludeMethod est un booléen, bIncludeURL est un booléen, bIncludeTimestamp est un booléen, bIncludeNonce est un booléen, bIncludeData est un booléen) _bIncludeMethod = bIncludeMethod _bIncludeURL = bIncludeURL _bIncludeTimestamp = bIncludeTimestamp _bIncludeNonce = bIncludeNonce _bIncludeData = bIncludeData FIN PROCEDURE PUBLIQUE SetCustomSignatureFormat(sFormat est une chaîne) _sCustomSignatureFormat = sFormat FIN // Sobrescrever métodos da classe base FONCTION PUBLIQUE ApplyAuth(tabHeaders est un tableau associatif de chaînes, tabQueryParams est un tableau associatif de chaînes, sMethod est une chaîne, sURL est une chaîne, sData est une chaîne) : booléen SI _sSecret = "" ALORS LogError("Chave secreta HMAC não configurada") RENVOYER Faux FIN // Gerar timestamp sTimestamp est une chaîne = RestUtils.GenerateTimestamp() // Gerar nonce sNonce est une chaîne = RestUtils.GenerateNonce() // Gerar a assinatura HMAC sSignature est une chaîne = GenerateSignature(sMethod, sURL, sData, sTimestamp, sNonce) // Adicionar os cabeçalhos necessários SI _bIncludeTimestamp ALORS tabHeaders[_sTimestampHeaderName] = sTimestamp FIN SI _bIncludeNonce ALORS tabHeaders[_sNonceHeaderName] = sNonce FIN tabHeaders[_sSignatureHeaderName] = sSignature LogDebug("Adicionados cabeçalhos de autenticação HMAC") RENVOYER Vrai FIN FONCTION PUBLIQUE IsAuthValid() : booléen RENVOYER _sSecret <> "" FIN PROCEDURE PUBLIQUE ClearAuth() _sSecret = "" FIN // Métodos específicos para HMAC FONCTION PUBLIQUE GenerateSignature(sMethod est une chaîne, sURL est une chaîne, sData est une chaîne, sTimestamp est une chaîne, sNonce est une chaîne) : chaîne // Verificar se está usando formato personalizado SI _sCustomSignatureFormat <> "" ALORS RENVOYER GenerateCustomSignature(sMethod, sURL, sData, sTimestamp, sNonce) FIN // Construir a string a ser assinada sStringToSign est une chaîne = "" // Incluir método HTTP SI _bIncludeMethod ALORS sStringToSign += sMethod + "\n" FIN // Incluir URL SI _bIncludeURL ALORS sStringToSign += sURL + "\n" FIN // Incluir timestamp SI _bIncludeTimestamp ALORS sStringToSign += sTimestamp + "\n" FIN // Incluir nonce SI _bIncludeNonce ALORS sStringToSign += sNonce + "\n" FIN // Incluir dados SI _bIncludeData ET sData <> "" ALORS sStringToSign += sData FIN // Remover último \n se existir SI Droite(sStringToSign, 1) = EOT ALORS sStringToSign = Gauche(sStringToSign, Taille(sStringToSign) - 1) FIN LogDebug("String a ser assinada: " + sStringToSign) // Calcular o HMAC usando o algoritmo especificado RENVOYER RestUtils.CalculateHMAC(sStringToSign, _sSecret, _sAlgorithm) FIN PRIVE FONCTION GenerateCustomSignature(sMethod est une chaîne, sURL est une chaîne, sData est une chaîne, sTimestamp est une chaîne, sNonce est une chaîne) : chaîne // Usar o formato personalizado, substituindo marcadores sStringToSign est une chaîne = _sCustomSignatureFormat sStringToSign = Remplace(sStringToSign, "{method}", sMethod) sStringToSign = Remplace(sStringToSign, "{url}", sURL) sStringToSign = Remplace(sStringToSign, "{timestamp}", sTimestamp) sStringToSign = Remplace(sStringToSign, "{nonce}", sNonce) sStringToSign = Remplace(sStringToSign, "{data}", sData) LogDebug("String a ser assinada (formato personalizado): " + sStringToSign) // Calcular o HMAC usando o algoritmo especificado RENVOYER RestUtils.CalculateHMAC(sStringToSign, _sSecret, _sAlgorithm) FIN // Método para verificar uma assinatura (útil para testes ou para implementar um servidor) FONCTION PUBLIQUE VerifySignature(sSignature est une chaîne, sMethod est une chaîne, sURL est une chaîne, sData est une chaîne, sTimestamp est une chaîne, sNonce est une chaîne) : booléen // Gerar assinatura com os mesmos parâmetros sExpectedSignature est une chaîne = GenerateSignature(sMethod, sURL, sData, sTimestamp, sNonce) // Comparar assinaturas RENVOYER sSignature = sExpectedSignature FIN // Método para gerar e verificar uma assinatura em um só lugar (para testes) FONCTION PUBLIQUE TestSignature(sMethod est une chaîne, sURL est une chaîne, sData est une chaîne) : booléen // Gerar timestamp e nonce sTimestamp est une chaîne = RestUtils.GenerateTimestamp() sNonce est une chaîne = RestUtils.GenerateNonce() // Gerar assinatura sSignature est une chaîne = GenerateSignature(sMethod, sURL, sData, sTimestamp, sNonce) // Verificar assinatura bResult est un booléen = VerifySignature(sSignature, sMethod, sURL, sData, sTimestamp, sNonce) LogDebug("Teste de assinatura HMAC: " + (bResult ? "SUCESSO" SINON "FALHA")) RENVOYER bResult FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:35 AM
// ------------------------------------------------------------------------------------------------ // RestAuthOAuth2.wdc - Autenticação OAuth 2.0 // ------------------------------------------------------------------------------------------------ // Implementa autenticação OAuth 2.0 com suporte a diferentes grant types // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestAuthOAuth2 HÉRITE DE RestAuth // Propriedades específicas PRIVE _sClientID est une chaîne _sClientSecret est une chaîne _sTokenEndpoint est une chaîne _sScope est une chaîne _sGrantType est une chaîne = "client_credentials" // Tipo padrão _sAccessToken est une chaîne _sRefreshToken est une chaîne _dhTokenExpiry est une dateheure _bHasTokenExpiry est un booléen = Faux _bAutoRefresh est un booléen = Vrai // Para Resource Owner Password Grant _sUsername est une chaîne _sPassword est une chaîne // Para Authorization Code Grant _sRedirectURI est une chaîne _sAuthorizationCode est une chaîne _sAuthorizationEndpoint est une chaîne // Construtores PROCEDURE PUBLIQUE Constructeur() Constructeur RestAuth(EAuthType.OAUTH2) FIN PROCEDURE PUBLIQUE Constructeur(sClientID est une chaîne, sClientSecret est une chaîne, sTokenEndpoint est une chaîne) Constructeur RestAuth(EAuthType.OAUTH2) _sClientID = sClientID _sClientSecret = sClientSecret _sTokenEndpoint = sTokenEndpoint FIN PROCEDURE PUBLIQUE Constructeur(sClientID est une chaîne, sClientSecret est une chaîne, sTokenEndpoint est une chaîne, sScope est une chaîne) Constructeur RestAuth(EAuthType.OAUTH2) _sClientID = sClientID _sClientSecret = sClientSecret _sTokenEndpoint = sTokenEndpoint _sScope = sScope FIN PROCEDURE PUBLIQUE Constructeur(sClientID est une chaîne, sClientSecret est une chaîne, sTokenEndpoint est une chaîne, sScope est une chaîne, logger est un RestLogger) Constructeur RestAuth(EAuthType.OAUTH2, logger) _sClientID = sClientID _sClientSecret = sClientSecret _sTokenEndpoint = sTokenEndpoint _sScope = sScope FIN // Métodos de configuração básica PROCEDURE PUBLIQUE SetClientCredentials(sClientID est une chaîne, sClientSecret est une chaîne) _sClientID = sClientID _sClientSecret = sClientSecret FIN PROCEDURE PUBLIQUE SetEndpoints(sTokenEndpoint est une chaîne, sAuthorizationEndpoint est une chaîne = "") _sTokenEndpoint = sTokenEndpoint _sAuthorizationEndpoint = sAuthorizationEndpoint FIN PROCEDURE PUBLIQUE SetScope(sScope est une chaîne) _sScope = sScope FIN PROCEDURE PUBLIQUE SetAutoRefresh(bAutoRefresh est un booléen) _bAutoRefresh = bAutoRefresh FIN // Configuração para diferentes Grant Types // Client Credentials Grant PROCEDURE PUBLIQUE SetupClientCredentialsGrant() _sGrantType = "client_credentials" _sUsername = "" _sPassword = "" _sRedirectURI = "" _sAuthorizationCode = "" FIN // Resource Owner Password Grant PROCEDURE PUBLIQUE SetupPasswordGrant(sUsername est une chaîne, sPassword est une chaîne) _sGrantType = "password" _sUsername = sUsername _sPassword = sPassword _sRedirectURI = "" _sAuthorizationCode = "" FIN // Authorization Code Grant PROCEDURE PUBLIQUE SetupAuthorizationCodeGrant(sRedirectURI est une chaîne) _sGrantType = "authorization_code" _sUsername = "" _sPassword = "" _sRedirectURI = sRedirectURI _sAuthorizationCode = "" FIN PROCEDURE PUBLIQUE SetAuthorizationCode(sAuthorizationCode est une chaîne) _sAuthorizationCode = sAuthorizationCode FIN // Refresh Token Grant PROCEDURE PUBLIQUE SetupRefreshTokenGrant(sRefreshToken est une chaîne) _sGrantType = "refresh_token" _sRefreshToken = sRefreshToken FIN // Métodos para gerenciar tokens PROCEDURE PUBLIQUE SetTokens(sAccessToken est une chaîne, sRefreshToken est une chaîne = "") _sAccessToken = sAccessToken _sRefreshToken = sRefreshToken _bHasTokenExpiry = Faux FIN PROCEDURE PUBLIQUE SetTokensWithExpiry(sAccessToken est une chaîne, sRefreshToken est une chaîne, dhExpiry est une dateheure) _sAccessToken = sAccessToken _sRefreshToken = sRefreshToken _dhTokenExpiry = dhExpiry _bHasTokenExpiry = Vrai FIN PROCEDURE PUBLIQUE SetTokensWithExpirySeconds(sAccessToken est une chaîne, sRefreshToken est une chaîne, nExpirySeconds est un entier) _sAccessToken = sAccessToken _sRefreshToken = sRefreshToken _dhTokenExpiry = DateHeureSys() + nExpirySeconds _bHasTokenExpiry = Vrai FIN // Getters para informações do token FONCTION PUBLIQUE GetAccessToken() : chaîne RENVOYER _sAccessToken FIN FONCTION PUBLIQUE GetRefreshToken() : chaîne RENVOYER _sRefreshToken FIN FONCTION PUBLIQUE GetTokenExpiry() : dateheure RENVOYER _dhTokenExpiry FIN FONCTION PUBLIQUE HasTokenExpiry() : booléen RENVOYER _bHasTokenExpiry FIN // Sobrescrever métodos da classe base FONCTION PUBLIQUE ApplyAuth(tabHeaders est un tableau associatif de chaînes, tabQueryParams est un tableau associatif de chaînes, sMethod est une chaîne, sURL est une chaîne, sData est une chaîne) : booléen // Verificar se o token está expirado e se auto-refresh está habilitado SI _bAutoRefresh ET PAS IsAuthValid() ALORS SI RefreshAuth() = Faux ALORS LogError("Falha ao renovar token OAuth2") RENVOYER Faux FIN FIN // Verificar se há token disponível SI _sAccessToken = "" ALORS LogError("Token OAuth2 não configurado") RENVOYER Faux FIN // Adicionar cabeçalho de autorização tabHeaders["Authorization"] = "Bearer " + _sAccessToken LogDebug("Adicionado cabeçalho de autenticação OAuth2 (Bearer)") RENVOYER Vrai FIN FONCTION PUBLIQUE IsAuthValid() : booléen SI _sAccessToken = "" ALORS RENVOYER Faux FIN // Verificar expiração SI _bHasTokenExpiry ET DateHeureSys() >= _dhTokenExpiry ALORS LogDebug("Token OAuth2 expirado") RENVOYER Faux FIN RENVOYER Vrai FIN FONCTION PUBLIQUE RefreshAuth() : booléen // Verificar configuração básica SI _sClientID = "" OU _sClientSecret = "" OU _sTokenEndpoint = "" ALORS LogError("Configuração OAuth2 incompleta") RENVOYER Faux FIN // Determinar qual grant type usar para refresh sCurrentGrantType est une chaîne = _sGrantType // Se temos refresh token, usar refresh_token grant SI _sRefreshToken <> "" ALORS sCurrentGrantType = "refresh_token" FIN // Configurar requisição HTTP para obter token reqHttp est un restRequête reqHttp.URL = _sTokenEndpoint reqHttp.Méthode = httpPost reqHttp.ContentType = "application/x-www-form-urlencoded" // Parâmetros comuns reqHttp.Paramètre["grant_type"] = sCurrentGrantType reqHttp.Paramètre["client_id"] = _sClientID reqHttp.Paramètre["client_secret"] = _sClientSecret SI _sScope <> "" ALORS reqHttp.Paramètre["scope"] = _sScope FIN // Parâmetros específicos por grant type SELON sCurrentGrantType CAS "refresh_token": reqHttp.Paramètre["refresh_token"] = _sRefreshToken CAS "password": reqHttp.Paramètre["username"] = _sUsername reqHttp.Paramètre["password"] = _sPassword CAS "authorization_code": reqHttp.Paramètre["code"] = _sAuthorizationCode reqHttp.Paramètre["redirect_uri"] = _sRedirectURI FIN // Executar requisição HTTP SI httpEnvoie(reqHttp) = Faux ALORS LogError("Falha na requisição para obter token OAuth2: " + ErreurInfo(errMessage)) RENVOYER Faux FIN // Verificar resposta SI reqHttp.CodeEtat < 200 OU reqHttp.CodeEtat >= 300 ALORS LogError("Erro HTTP ao obter token OAuth2: " + NumériqueVersChaine(reqHttp.CodeEtat) + " - " + reqHttp.Réponse) RENVOYER Faux FIN // Processar a resposta em JSON tokenResponse est un JSON QUAND EXCEPTION DANS tokenResponse = JSONVersVariant(reqHttp.Réponse) FAIRE LogError("Resposta inválida do servidor OAuth2: " + reqHttp.Réponse) RENVOYER Faux FIN // Verificar e processar token SI tokenResponse.access_token = Null OU tokenResponse.access_token = "" ALORS LogError("Resposta OAuth2 não contém access_token") RENVOYER Faux FIN // Salvar o access token _sAccessToken = tokenResponse.access_token // Verificar se há refresh token SI tokenResponse.refresh_token <> Null ET tokenResponse.refresh_token <> "" ALORS _sRefreshToken = tokenResponse.refresh_token FIN // Verificar se há informação de expiração SI tokenResponse.expires_in <> Null ET tokenResponse.expires_in > 0 ALORS nExpiresIn est un entier = tokenResponse.expires_in _dhTokenExpiry = DateHeureSys() + nExpiresIn _bHasTokenExpiry = Vrai FIN LogDebug("Token OAuth2 obtido/renovado com sucesso") RENVOYER Vrai FIN PROCEDURE PUBLIQUE ClearAuth() _sAccessToken = "" _sRefreshToken = "" _bHasTokenExpiry = Faux FIN // Métodos adicionais específicos para OAuth 2.0 // Gera a URL de autorização para Authorization Code Grant FONCTION PUBLIQUE GetAuthorizationURL(sState est une chaîne = "") : chaîne SI _sAuthorizationEndpoint = "" OU _sClientID = "" OU _sRedirectURI = "" ALORS LogError("Configuração incompleta para URL de autorização") RENVOYER "" FIN sURL est une chaîne = _sAuthorizationEndpoint sURL += "?response_type=code" sURL += "&client_id=" + RestUtils.URLEncode(_sClientID) sURL += "&redirect_uri=" + RestUtils.URLEncode(_sRedirectURI) SI _sScope <> "" ALORS sURL += "&scope=" + RestUtils.URLEncode(_sScope) FIN SI sState <> "" ALORS sURL += "&state=" + RestUtils.URLEncode(sState) FIN RENVOYER sURL FIN // Extrai o authorization code de uma URL de redirecionamento (para Authorization Code Grant) FONCTION PUBLIQUE ExtractAuthorizationCode(sRedirectURL est une chaîne) : chaîne // Procurar o parâmetro 'code' na URL sCode est une chaîne = ExtraitParametre(sRedirectURL, "code") SI sCode <> "" ALORS _sAuthorizationCode = sCode FIN RENVOYER sCode FIN // Função para extrair parâmetro de uma URL PRIVE FONCTION ExtraitParametre(sURL est une chaîne, sParamName est une chaîne) : chaîne // Encontrar o parâmetro na query string nPos est un entier = Position(sURL, "?" + sParamName + "=") SI nPos = 0 ALORS nPos = Position(sURL, "&" + sParamName + "=") FIN SI nPos = 0 ALORS RENVOYER "" FIN // Encontrar o valor do parâmetro sQueryPart est une chaîne = Milieu(sURL, nPos + Taille(sParamName) + 2) // Verificar se há mais parâmetros depois nEndPos est un entier = Position(sQueryPart, "&") SI nEndPos > 0 ALORS RENVOYER Gauche(sQueryPart, nEndPos - 1) SINON RENVOYER sQueryPart FIN FIN // Configura a autenticação OAuth 2.0 a partir de um token já existente (por exemplo, armazenado em cache) PROCEDURE PUBLIQUE SetupFromExistingToken(sAccessToken est une chaîne, sRefreshToken est une chaîne = "", nExpirySeconds est un entier = 0) _sAccessToken = sAccessToken _sRefreshToken = sRefreshToken SI nExpirySeconds > 0 ALORS _dhTokenExpiry = DateHeureSys() + nExpirySeconds _bHasTokenExpiry = Vrai SINON _bHasTokenExpiry = Faux FIN LogDebug("OAuth2 configurado a partir de token existente") FIN // Método para validar um token localmente (verificação básica) FONCTION PUBLIQUE ValidateTokenFormat() : booléen SI _sAccessToken = "" ALORS RENVOYER Faux FIN // Verificação básica para token JWT SI Gauche(_sAccessToken, 3) = "ey" ALORS // Parece ser um token JWT, verificar formato tabParts est un tableau de chaînes = ExtraitChaîne(_sAccessToken, ".", 1, 3) // JWT válido deve ter 3 partes separadas por ponto SI TableauOccurrence(tabParts) <> 3 ALORS LogDebug("Token não possui formato JWT válido (deve ter três partes separadas por ponto)") RENVOYER Faux FIN // Tentar decodificar o payload (segunda parte) QUAND EXCEPTION DANS sPayload est une chaîne = Decode(tabParts[2], encodeBASE64) // Verificar se é um JSON válido payloadJSON est un JSON = JSONVersVariant(sPayload) // Se tiver expiração, atualizar o tempo de expiração SI payloadJSON.exp <> Null ET TypeVar(payloadJSON.exp) = typNumérique ALORS _dhTokenExpiry = DateHeureVersDateHeure(payloadJSON.exp) _bHasTokenExpiry = Vrai LogDebug("Expiração do token extraída do payload JWT") FIN RENVOYER Vrai FAIRE LogDebug("Payload do token não pode ser decodificado como um JWT válido") RENVOYER Faux FIN FIN // Se não for JWT, considerar válido se não estiver vazio RENVOYER Vrai FIN // Método para validar o token contra o servidor (introspection endpoint) FONCTION PUBLIQUE ValidateTokenWithServer(sIntrospectionEndpoint est une chaîne) : booléen SI _sAccessToken = "" OU sIntrospectionEndpoint = "" ALORS RENVOYER Faux FIN // Configurar requisição para introspection endpoint reqHttp est un restRequête reqHttp.URL = sIntrospectionEndpoint reqHttp.Méthode = httpPost reqHttp.ContentType = "application/x-www-form-urlencoded" // Adicionar autenticação do cliente reqHttp.Entête["Authorization"] = "Basic " + Encode(_sClientID + ":" + _sClientSecret, encodeBASE64) // Parâmetros para introspection reqHttp.Paramètre["token"] = _sAccessToken reqHttp.Paramètre["token_type_hint"] = "access_token" // Executar requisição SI httpEnvoie(reqHttp) = Faux ALORS LogError("Falha na requisição para validar token: " + ErreurInfo(errMessage)) RENVOYER Faux FIN // Verificar resposta SI reqHttp.CodeEtat < 200 OU reqHttp.CodeEtat >= 300 ALORS LogError("Erro HTTP ao validar token: " + NumériqueVersChaine(reqHttp.CodeEtat)) RENVOYER Faux FIN // Processar a resposta introspectionResponse est un JSON QUAND EXCEPTION DANS introspectionResponse = JSONVersVariant(reqHttp.Réponse) FAIRE LogError("Resposta inválida do servidor de introspection: " + reqHttp.Réponse) RENVOYER Faux FIN // Verificar se o token está ativo SI introspectionResponse.active <> Null ET introspectionResponse.active = Faux ALORS LogDebug("Token reportado como inativo pelo servidor") RENVOYER Faux FIN // Atualizar informações de expiração, se disponíveis SI introspectionResponse.exp <> Null ET TypeVar(introspectionResponse.exp) = typNumérique ALORS _dhTokenExpiry = DateHeureVersDateHeure(introspectionResponse.exp) _bHasTokenExpiry = Vrai FIN LogDebug("Token validado com sucesso pelo servidor") RENVOYER Vrai FIN // Método para revogar um token (se o servidor suportar) FONCTION PUBLIQUE RevokeToken(sRevocationEndpoint est une chaîne, bRevokeRefreshToken est un booléen = Faux) : booléen // Verificar parâmetros SI (bRevokeRefreshToken ET _sRefreshToken = "") OU (PAS bRevokeRefreshToken ET _sAccessToken = "") OU sRevocationEndpoint = "" ALORS RENVOYER Faux FIN // Configurar requisição para revocation endpoint reqHttp est un restRequête reqHttp.URL = sRevocationEndpoint reqHttp.Méthode = httpPost reqHttp.ContentType = "application/x-www-form-urlencoded" // Adicionar autenticação do cliente reqHttp.Entête["Authorization"] = "Basic " + Encode(_sClientID + ":" + _sClientSecret, encodeBASE64) // Determinar qual token revogar SI bRevokeRefreshToken ALORS reqHttp.Paramètre["token"] = _sRefreshToken reqHttp.Paramètre["token_type_hint"] = "refresh_token" SINON reqHttp.Paramètre["token"] = _sAccessToken reqHttp.Paramètre["token_type_hint"] = "access_token" FIN // Executar requisição SI httpEnvoie(reqHttp) = Faux ALORS LogError("Falha na requisição para revogar token: " + ErreurInfo(errMessage)) RENVOYER Faux FIN // Verificar resposta - muitos servidores retornam 200 OK sem conteúdo SI reqHttp.CodeEtat < 200 OU reqHttp.CodeEtat >= 300 ALORS LogError("Erro HTTP ao revogar token: " + NumériqueVersChaine(reqHttp.CodeEtat)) RENVOYER Faux FIN // Se chegou aqui, o token foi revogado com sucesso SI bRevokeRefreshToken ALORS _sRefreshToken = "" LogDebug("Refresh token revogado com sucesso") SINON _sAccessToken = "" LogDebug("Access token revogado com sucesso") FIN RENVOYER Vrai FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:38 AM
// ------------------------------------------------------------------------------------------------ // RestAuthApiKey.wdc - Autenticação API Key // ------------------------------------------------------------------------------------------------ // Implementa autenticação com API Key (no header ou query param) // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestAuthApiKey HÉRITE DE RestAuth // Propriedades específicas PRIVE _sApiKey est une chaîne _sApiKeyName est une chaîne _bInHeader est un booléen = Vrai // Construtores PROCEDURE PUBLIQUE Constructeur() Constructeur RestAuth(EAuthType.API_KEY) _sApiKey = "" _sApiKeyName = "X-API-Key" // Nome padrão FIN PROCEDURE PUBLIQUE Constructeur(sApiKey est une chaîne, sApiKeyName est une chaîne, bInHeader est un booléen = Vrai) Constructeur RestAuth(EAuthType.API_KEY) _sApiKey = sApiKey _sApiKeyName = sApiKeyName _bInHeader = bInHeader FIN PROCEDURE PUBLIQUE Constructeur(sApiKey est une chaîne, sApiKeyName est une chaîne, bInHeader est un booléen, logger est un RestLogger) Constructeur RestAuth(EAuthType.API_KEY, logger) _sApiKey = sApiKey _sApiKeyName = sApiKeyName _bInHeader = bInHeader FIN // Métodos públicos de configuração PROCEDURE PUBLIQUE SetApiKey(sApiKey est une chaîne) _sApiKey = sApiKey FIN PROCEDURE PUBLIQUE SetApiKeyName(sApiKeyName est une chaîne) _sApiKeyName = sApiKeyName FIN PROCEDURE PUBLIQUE SetInHeader(bInHeader est un booléen) _bInHeader = bInHeader FIN FONCTION PUBLIQUE GetApiKey() : chaîne RENVOYER _sApiKey FIN FONCTION PUBLIQUE GetApiKeyName() : chaîne RENVOYER _sApiKeyName FIN FONCTION PUBLIQUE IsInHeader() : booléen RENVOYER _bInHeader FIN // Sobrescrever métodos da classe base FONCTION PUBLIQUE ApplyAuth(tabHeaders est un tableau associatif de chaînes, tabQueryParams est un tableau associatif de chaînes, sMethod est une chaîne, sURL est une chaîne, sData est une chaîne) : booléen SI _sApiKey = "" OU _sApiKeyName = "" ALORS LogError("API Key ou nome da API Key não configurado") RENVOYER Faux FIN // Adicionar a API Key ao cabeçalho ou parâmetro de consulta SI _bInHeader ALORS tabHeaders[_sApiKeyName] = _sApiKey LogDebug("API Key adicionada ao cabeçalho: " + _sApiKeyName) SINON tabQueryParams[_sApiKeyName] = _sApiKey LogDebug("API Key adicionada ao parâmetro de consulta: " + _sApiKeyName) FIN RENVOYER Vrai FIN FONCTION PUBLIQUE IsAuthValid() : booléen RENVOYER _sApiKey <> "" ET _sApiKeyName <> "" FIN PROCEDURE PUBLIQUE ClearAuth() _sApiKey = "" FIN // Métodos adicionais específicos para API Key // Método para alternar a posição da API Key entre header e query param PROCEDURE PUBLIQUE TogglePosition() _bInHeader = PAS _bInHeader LogDebug("Posição da API Key alternada para " + (_bInHeader ? "cabeçalho" SINON "parâmetro de consulta")) FIN // Método para gerar uma API Key aleatória (para testes ou gerenciamento de chaves) FONCTION STATIQUE GenerateRandomApiKey(nLength est un entier = 32) : chaîne RENVOYER ExtraitChaîne(Encode(ChaîneAlexandrie(nLength), encodeBASE64), "=", 1) FIN // Obter a configuração completa como string (para debug/log) FONCTION PUBLIQUE GetConfigAsString() : chaîne RENVOYER "API Key '" + _sApiKeyName + "' " + (_bInHeader ? "no cabeçalho" SINON "no parâmetro de consulta") + ", Chave: " + (_sApiKey = "" ? "<não definida>" SINON Gauche(_sApiKey, 3) + "..." + Droite(_sApiKey, 3)) FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:41 AM
// ------------------------------------------------------------------------------------------------ // RestAuthBearer.wdc - Autenticação Bearer Token // ------------------------------------------------------------------------------------------------ // Implementa autenticação com Bearer Token (JWT) // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestAuthBearer HÉRITE DE RestAuth // Propriedades específicas PRIVE _sToken est une chaîne _dhExpiry est une dateheure _bHasExpiry est un booléen = Faux // Construtores PROCEDURE PUBLIQUE Constructeur() Constructeur RestAuth(EAuthType.BEARER) _sToken = "" FIN PROCEDURE PUBLIQUE Constructeur(sToken est une chaîne) Constructeur RestAuth(EAuthType.BEARER) _sToken = sToken FIN PROCEDURE PUBLIQUE Constructeur(sToken est une chaîne, logger est un RestLogger) Constructeur RestAuth(EAuthType.BEARER, logger) _sToken = sToken FIN // Métodos públicos de configuração PROCEDURE PUBLIQUE SetToken(sToken est une chaîne) _sToken = sToken _bHasExpiry = Faux // Reset expiry info FIN PROCEDURE PUBLIQUE SetTokenWithExpiry(sToken est une chaîne, dhExpiry est une dateheure) _sToken = sToken _dhExpiry = dhExpiry _bHasExpiry = Vrai FIN PROCEDURE PUBLIQUE SetTokenWithExpirySeconds(sToken est une chaîne, nExpirySeconds est un entier) _sToken = sToken _dhExpiry = DateHeureSys() + nExpirySeconds _bHasExpiry = Vrai FIN FONCTION PUBLIQUE GetToken() : chaîne RENVOYER _sToken FIN FONCTION PUBLIQUE HasExpiry() : booléen RENVOYER _bHasExpiry FIN FONCTION PUBLIQUE GetExpiry() : dateheure RENVOYER _dhExpiry FIN // Sobrescrever métodos da classe base FONCTION PUBLIQUE ApplyAuth(tabHeaders est un tableau associatif de chaînes, tabQueryParams est un tableau associatif de chaînes, sMethod est une chaîne, sURL est une chaîne, sData est une chaîne) : booléen SI _sToken = "" ALORS LogError("Token Bearer não configurado") RENVOYER Faux FIN // Adicionar cabeçalho de autorização tabHeaders["Authorization"] = "Bearer " + _sToken LogDebug("Adicionado cabeçalho de autenticação Bearer") RENVOYER Vrai FIN FONCTION PUBLIQUE IsAuthValid() : booléen SI _sToken = "" ALORS RENVOYER Faux FIN // Verificar expiração se aplicável SI _bHasExpiry ALORS SI DateHeureSys() >= _dhExpiry ALORS LogDebug("Token Bearer expirado") RENVOYER Faux FIN FIN RENVOYER Vrai FIN PROCEDURE PUBLIQUE ClearAuth() _sToken = "" _bHasExpiry = Faux FIN // Método para verificar se o token é um JWT válido (estrutura básica) FONCTION PUBLIQUE IsValidJWTFormat() : booléen SI _sToken = "" ALORS RENVOYER Faux FIN // Verificação básica de formato JWT (header.payload.signature) tabParts est un tableau de chaînes = ExtraitChaîne(_sToken, ".", 1, 3) // JWT válido deve ter 3 partes separadas por ponto SI TableauOccurrence(tabParts) <> 3 ALORS LogDebug("Token não possui formato JWT válido (deve ter três partes separadas por ponto)") RENVOYER Faux FIN // Verificar se cada parte pode ser decodificada em Base64 POUR i = 1 À 3 QUAND EXCEPTION DANS sTemp est une chaîne = Decode(tabParts[i], encodeBASE64) FAIRE LogDebug("Parte " + NumériqueVersChaine(i) + " do token não é válida Base64") RENVOYER Faux FIN FIN // Tentar decodificar o payload (segunda parte) jwtPayload est une chaîne = Decode(tabParts[2], encodeBASE64) // Verificar se o payload é um JSON válido QUAND EXCEPTION DANS payloadJSON est un JSON = JSONVersVariant(jwtPayload) // Verificar se há claim de expiração e atualizar se necessário SI payloadJSON.exp <> Null ET _bHasExpiry = Faux ALORS nExpTimestamp est un entier = payloadJSON.exp _dhExpiry = DateHeureVersDateHeure(nExpTimestamp) _bHasExpiry = Vrai LogDebug("Expiração do token atualizada do payload JWT") FIN RENVOYER Vrai FAIRE LogDebug("Payload do token não é um JSON válido") RENVOYER Faux FIN FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:43 AM
// ------------------------------------------------------------------------------------------------ // RestLogger.wdc - Classe de Logger // ------------------------------------------------------------------------------------------------ // Implementa um sistema de log para a biblioteca RestAPI // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestLogger // Propriedades PRIVE _bEnabled est un booléen = Faux _nLogLevel est un ELogLevel = ELogLevel.INFO _sLogPath est une chaîne = fRepDonnées() + [fSep] + "logs" _sLogFilePrefix est une chaîne = "restapi_" _bConsoleOutput est un booléen = Faux _bFileOutput est un booléen = Vrai // Construtores PROCEDURE PUBLIQUE Constructeur() // Construtor padrão FIN PROCEDURE PUBLIQUE Constructeur(bEnabled est un booléen) _bEnabled = bEnabled // Criar diretório de logs se não existir EnsureLogDirectory() FIN PROCEDURE PUBLIQUE Constructeur(bEnabled est un booléen, nLogLevel est un ELogLevel) _bEnabled = bEnabled _nLogLevel = nLogLevel // Criar diretório de logs se não existir EnsureLogDirectory() FIN PROCEDURE PUBLIQUE Constructeur(bEnabled est un booléen, nLogLevel est un ELogLevel, sLogPath est une chaîne) _bEnabled = bEnabled _nLogLevel = nLogLevel _sLogPath = sLogPath // Criar diretório de logs se não existir EnsureLogDirectory() FIN // Métodos de configuração PROCEDURE PUBLIQUE SetEnabled(bEnabled est un booléen) _bEnabled = bEnabled FIN PROCEDURE PUBLIQUE SetLogLevel(nLogLevel est un ELogLevel) _nLogLevel = nLogLevel FIN PROCEDURE PUBLIQUE SetLogPath(sLogPath est une chaîne) _sLogPath = sLogPath EnsureLogDirectory() FIN PROCEDURE PUBLIQUE SetLogFilePrefix(sPrefix est une chaîne) _sLogFilePrefix = sPrefix FIN PROCEDURE PUBLIQUE SetConsoleOutput(bEnable est un booléen) _bConsoleOutput = bEnable FIN PROCEDURE PUBLIQUE SetFileOutput(bEnable est un booléen) _bFileOutput = bEnable FIN // Métodos de logging para cada nível PROCEDURE PUBLIQUE Debug(sMessage est une chaîne) SI _bEnabled ET _nLogLevel <= ELogLevel.DEBUG ALORS WriteLog(sMessage, ELogLevel.DEBUG) FIN FIN PROCEDURE PUBLIQUE Info(sMessage est une chaîne) SI _bEnabled ET _nLogLevel <= ELogLevel.INFO ALORS WriteLog(sMessage, ELogLevel.INFO) FIN FIN PROCEDURE PUBLIQUE Warning(sMessage est une chaîne) SI _bEnabled ET _nLogLevel <= ELogLevel.WARNING ALORS WriteLog(sMessage, ELogLevel.WARNING) FIN FIN PROCEDURE PUBLIQUE Error(sMessage est une chaîne) SI _bEnabled ET _nLogLevel <= ELogLevel.ERROR ALORS WriteLog(sMessage, ELogLevel.ERROR) FIN FIN // Métodos para logging de requisições e respostas HTTP PROCEDURE PUBLIQUE LogRequest(sMethod est une chaîne, sURL est une chaîne, tabHeaders est un tableau associatif de chaînes, sData est une chaîne) SI PAS _bEnabled OU _nLogLevel > ELogLevel.DEBUG ALORS RETOUR FIN sLogMessage est une chaîne = "REQUEST: " + sMethod + " " + sURL + EOT // Adicionar headers sLogMessage += "HEADERS:" + EOT POUR TOUT pHeader DE tabHeaders sLogMessage += " " + pHeader..Nom + ": " + pHeader + EOT FIN // Adicionar dados SI sData <> "" ALORS sLogMessage += "DATA:" + EOT + sData FIN Debug(sLogMessage) FIN PROCEDURE PUBLIQUE LogResponse(nStatusCode est un entier, sResponseBody est une chaîne, tabHeaders est un tableau associatif de chaînes) SI PAS _bEnabled OU _nLogLevel > ELogLevel.DEBUG ALORS RETOUR FIN sLogMessage est une chaîne = "RESPONSE [" + NumériqueVersChaine(nStatusCode) + "]" + EOT // Adicionar headers sLogMessage += "HEADERS:" + EOT POUR TOUT pHeader DE tabHeaders sLogMessage += " " + pHeader..Nom + ": " + pHeader + EOT FIN // Adicionar corpo da resposta SI sResponseBody <> "" ALORS SI Taille(sResponseBody) > 2000 ALORS // Truncar resposta muito grande sLogMessage += "BODY: (truncado a 2000 chars)" + EOT + Gauche(sResponseBody, 2000) + "..." SINON sLogMessage += "BODY:" + EOT + sResponseBody FIN FIN Debug(sLogMessage) FIN PROCEDURE PUBLIQUE LogError(sErrorMessage est une chaîne, sAdditionalInfo est une chaîne = "") SI PAS _bEnabled OU _nLogLevel > ELogLevel.ERROR ALORS RETOUR FIN sLogMessage est une chaîne = "ERROR: " + sErrorMessage SI sAdditionalInfo <> "" ALORS sLogMessage += EOT + "ADDITIONAL INFO: " + sAdditionalInfo FIN Error(sLogMessage) FIN // Métodos privados PRIVE PROCEDURE WriteLog(sMessage est une chaîne, nLevel est un ELogLevel) // Obter data e hora atual formatada dhNow est une dateheure = DateHeureSys() sDateTime est une chaîne = DateHeureVersChaîne(dhNow, "yyyy-MM-dd HH:mm:ss.fff") // Obter label do nível de log sLevelLabel est une chaîne = GetLogLevelLabel(nLevel) // Montar mensagem completa sFullMessage est une chaîne = "[" + sDateTime + "] [" + sLevelLabel + "] " + sMessage // Saída para console se habilitado SI _bConsoleOutput ALORS Trace(sFullMessage) FIN // Saída para arquivo se habilitado SI _bFileOutput ALORS // Obter nome do arquivo de log para o dia atual sLogFileName est une chaîne = _sLogPath + [fSep] + _sLogFilePrefix + DateVersChaîne(dhNow, "yyyyMMdd") + ".log" // Escrever no arquivo de log fAjouteLigne(sLogFileName, sFullMessage) FIN FIN PRIVE PROCEDURE EnsureLogDirectory() // Verificar se o diretório de logs existe, senão criar SI _bEnabled ET _bFileOutput ET PAS fRepertoireExiste(_sLogPath) ALORS fRepCrée(_sLogPath) FIN FIN PRIVE FONCTION GetLogLevelLabel(nLevel est un ELogLevel) : chaîne SELON nLevel CAS ELogLevel.DEBUG: RENVOYER "DEBUG" CAS ELogLevel.INFO: RENVOYER "INFO" CAS ELogLevel.WARNING: RENVOYER "WARN" CAS ELogLevel.ERROR: RENVOYER "ERROR" AUTRE CAS: RENVOYER "UNKNOWN" FIN FIN FIN -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 2:45 AM
// ------------------------------------------------------------------------------------------------ // RestAuth.wdc - Classe Base de Autenticação // ------------------------------------------------------------------------------------------------ // Classe abstrata que define a interface para todos os métodos de autenticação // Autor: Claude // Data: 03/04/2025 // ------------------------------------------------------------------------------------------------ CLASSE RestAuth // Propriedades base PROTÉGÉ _nAuthType est un EAuthType _logger est un RestLogger // Construtor PROCEDURE PUBLIQUE Constructeur() _nAuthType = EAuthType.NONE _logger = Null FIN PROCEDURE PUBLIQUE Constructeur(nAuthType est un EAuthType) _nAuthType = nAuthType _logger = Null FIN PROCEDURE PUBLIQUE Constructeur(nAuthType est un EAuthType, logger est un RestLogger) _nAuthType = nAuthType _logger = logger FIN // Configuração do logger PROCEDURE PUBLIQUE SetLogger(logger est un RestLogger) _logger = logger FIN // Método para obter o tipo de autenticação -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 6:22 AM
Versao estavel 7.0 // Classe facilitadora para integração com APIs REST em WLanguage com suporte a autenticação Classe_OOP_API_REST_Auth IS Class // --- Constantes globais para mensagens ao usuário --- CONSTANT MSG_ERROR_GET IS string = "Error in GET request: " CONSTANT MSG_ERROR_POST IS string = "Error in POST request: " CONSTANT MSG_ERROR_PUT IS string = "Error in PUT request: " CONSTANT MSG_ERROR_DELETE IS string = "Error in DELETE request: " CONSTANT MSG_ERROR_PATCH IS string = "Error in PATCH request: " CONSTANT MSG_ERROR_UPLOAD IS string = "Error in file upload: " CONSTANT MSG_ERROR_DOWNLOAD IS string = "Error in file download: " CONSTANT MSG_ERROR_OAUTH IS string = "Failed to obtain OAuth 2.0 token: " CONSTANT MSG_ERROR_AUTH_INVALID IS string = "Invalid authentication type" CONSTANT MSG_NO_RESPONSE IS string = "No response" // --- Propriedades privadas da classe --- _BaseURL IS string _Headers IS associative array OF strings _LastError IS string _Timeout IS int = 30 _AuthType IS string _Username IS string _Password IS string _Token IS string _APIKeyName IS string _OAuthClientID IS string _OAuthClientSecret IS string _OAuthTokenURL IS string _OAuthAccessToken IS string _OAuthRefreshToken IS string // Novo: Token de atualização para OAuth 2.0 _QueryParams IS associative array OF strings _RetryCount IS int = 0 // Novo: Número de retentativas _RetryDelay IS int = 2 // Novo: Atraso entre retentativas (segundos) // --- Construtor da classe --- PROCEDURE Constructor(baseURL IS string) _BaseURL = baseURL _Headers["Content-Type"] = "application/json" _AuthType = "None" _QueryParams = AssociativeArray() END // --- Configuração de autenticação Basic --- PROCEDURE ConfigureBasicAuth(username IS string, password IS string) _AuthType = "Basic" _Username = username _Password = password END // --- Configuração de autenticação Bearer Token --- PROCEDURE ConfigureBearerToken(token IS string) _AuthType = "Bearer" _Token = token END // --- Configuração de autenticação API Key --- PROCEDURE ConfigureAPIKey(keyName IS string, keyValue IS string) _AuthType = "APIKey" _APIKeyName = keyName _Token = keyValue END // --- Configuração de autenticação OAuth 2.0 (com refresh token) --- PROCEDURE ConfigureOAuth2(clientID IS string, clientSecret IS string, tokenURL IS string, refreshToken IS string = "") _AuthType = "OAuth2" _OAuthClientID = clientID _OAuthClientSecret = clientSecret _OAuthTokenURL = tokenURL _OAuthAccessToken = "" _OAuthRefreshToken = refreshToken // Armazena o refresh token, se fornecido END // --- Método privado para aplicar autenticação --- PROCEDURE _ApplyAuthentication(req IS HTTPRequest) PRIVATE SWITCH _AuthType CASE "Basic" auth IS string = Base64Encode(_Username + ":" + _Password) HTTPAddHeader(req, "Authorization", "Basic " + auth) CASE "Bearer" HTTPAddHeader(req, "Authorization", "Bearer " + _Token) CASE "APIKey" IF _APIKeyName <> "" THEN HTTPAddHeader(req, _APIKeyName, _Token) END CASE "OAuth2" IF _OAuthAccessToken = "" THEN _GetOAuth2Token() END HTTPAddHeader(req, "Authorization", "Bearer " + _OAuthAccessToken) CASE "None" OTHER CASE _LastError = MSG_ERROR_AUTH_INVALID END END // --- Método privado para obter ou atualizar token OAuth 2.0 --- PROCEDURE _GetOAuth2Token() PRIVATE req IS HTTPRequest req:URL = _OAuthTokenURL req:Method = httpPost req:Timeout = _Timeout HTTPAddHeader(req, "Content-Type", "application/x-www-form-urlencoded") // Se houver refresh token, tenta atualizar; senão, usa Client Credentials IF _OAuthRefreshToken <> "" THEN body IS string = "grant_type=refresh_token&refresh_token=" + _OAuthRefreshToken + "&client_id=" + _OAuthClientID + "&client_secret=" + _OAuthClientSecret ELSE body IS string = "grant_type=client_credentials&client_id=" + _OAuthClientID + "&client_secret=" + _OAuthClientSecret END req:Content = body res IS HTTPResponse = HTTPSend(req) IF res <> NULL AND res:StatusCode = 200 THEN responseData IS variant = JSONToVariant(res:Content) _OAuthAccessToken = responseData:access_token // Atualiza o refresh token, se retornado IF responseData:refresh_token <> "" THEN _OAuthRefreshToken = responseData:refresh_token END ELSE _LastError = MSG_ERROR_OAUTH + IF res <> NULL THEN res:StatusCode ELSE MSG_NO_RESPONSE END END // --- Método para adicionar cabeçalho --- PROCEDURE AddHeader(key IS string, value IS string) _Headers[key] = value END // --- Método para adicionar parâmetro de query --- PROCEDURE AddQueryParam(key IS string, value IS string) _QueryParams[key] = value END // --- Método para configurar retentativas --- PROCEDURE SetRetryPolicy(retryCount IS int, retryDelay IS int) _RetryCount = retryCount _RetryDelay = retryDelay END // --- Método privado para construir URL --- PROCEDURE _BuildURL(endpoint IS string) RETURNS string PRIVATE fullURL IS string = _BaseURL + endpoint IF _QueryParams:Count() > 0 THEN query IS string = "?" i IS int = 0 FOR EACH key, value OF _QueryParams IF i > 0 THEN query += "&" END query += key + "=" + URLEncode(value) i++ END fullURL += query END RETURN fullURL END // --- Método privado para executar requisição com retentativas --- PROCEDURE _ExecuteRequest(req IS HTTPRequest) RETURNS HTTPResponse PRIVATE attempt IS int = 0 WHILE attempt <= _RetryCount res IS HTTPResponse = HTTPSend(req) IF res <> NULL AND res:StatusCode IN (200, 201, 204) THEN RETURN res END attempt++ IF attempt <= _RetryCount THEN Sleep(_RetryDelay * 1000) // Aguarda antes da próxima tentativa END END RETURN res END // --- Método GET --- PROCEDURE Get(endpoint IS string) RETURNS variant fullURL IS string = _BuildURL(endpoint) req IS HTTPRequest req:URL = fullURL req:Method = httpGet req:Timeout = _Timeout _ApplyAuthentication(req) FOR EACH key, value OF _Headers HTTPAddHeader(req, key, value) END res IS HTTPResponse = _ExecuteRequest(req) IF res <> NULL AND res:StatusCode = 200 THEN RETURN JSONToVariant(res:Content) ELSE _LastError = MSG_ERROR_GET + IF res <> NULL THEN res:StatusCode ELSE MSG_NO_RESPONSE RETURN NULL END END // --- Método POST --- PROCEDURE Post(endpoint IS string, data IS variant) RETURNS variant fullURL IS string = _BuildURL(endpoint) req IS HTTPRequest req:URL = fullURL req:Method = httpPost req:Timeout = _Timeout req:Content = VariantToJSON(data) _ApplyAuthentication(req) FOR EACH key, value OF _Headers HTTPAddHeader(req, key, value) END res IS HTTPResponse = _ExecuteRequest(req) IF res <> NULL AND res:StatusCode = 201 THEN RETURN JSONToVariant(res:Content) ELSE _LastError = MSG_ERROR_POST + IF res <> NULL THEN res:StatusCode ELSE MSG_NO_RESPONSE RETURN NULL END END // --- Método PUT --- PROCEDURE Put(endpoint IS string, data IS variant) RETURNS variant fullURL IS string = _BuildURL(endpoint) req IS HTTPRequest req:URL = fullURL req:Method = httpPut req:Timeout = _Timeout req:Content = VariantToJSON(data) _ApplyAuthentication(req) FOR EACH key, value OF _Headers HTTPAddHeader(req, key, value) END res IS HTTPResponse = _ExecuteRequest(req) IF res <> NULL AND res:StatusCode = 200 THEN RETURN JSONToVariant(res:Content) ELSE _LastError = MSG_ERROR_PUT + IF res <> NULL THEN res:StatusCode ELSE MSG_NO_RESPONSE RETURN NULL END END // --- Método DELETE --- PROCEDURE Delete(endpoint IS string) RETURNS boolean fullURL IS string = _BuildURL(endpoint) req IS HTTPRequest req:URL = fullURL req:Method = httpDelete req:Timeout = _Timeout _ApplyAuthentication(req) FOR EACH key, value OF _Headers HTTPAddHeader(req, key, value) END res IS HTTPResponse = _ExecuteRequest(req) IF res <> NULL AND res:StatusCode = 204 THEN RETURN True ELSE _LastError = MSG_ERROR_DELETE + IF res <> NULL THEN res:StatusCode ELSE MSG_NO_RESPONSE RETURN False END END // --- Método PATCH --- PROCEDURE Patch(endpoint IS string, data IS variant) RETURNS variant fullURL IS string = _BuildURL(endpoint) req IS HTTPRequest req:URL = fullURL req:Method = httpPatch req:Timeout = _Timeout req:Content = VariantToJSON(data) _ApplyAuthentication(req) FOR EACH key, value OF _Headers HTTPAddHeader(req, key, value) END res IS HTTPResponse = _ExecuteRequest(req) IF res <> NULL AND res:StatusCode = 200 THEN RETURN JSONToVariant(res:Content) ELSE _LastError = MSG_ERROR_PATCH + IF res <> NULL THEN res:StatusCode ELSE MSG_NO_RESPONSE RETURN NULL END END // --- Método para upload de arquivos --- - httpGet req:Timeout = _Timeout _ApplyAuthentication(req) FOR EACH key, value OF _Headers HTTPAddHeader(req, key, value) END res IS HTTPResponse = _ExecuteRequest(req) IF res <> NULL AND res:StatusCode = 200 THEN fSaveFile(destinationPath, res:Content) RETURN True ELSE _LastError = MSG_ERROR_DOWNLOAD + IF res <> NULL THEN res:StatusCode ELSE MSG_NO_RESPONSE RETURN False END END // --- Método para obter o último erro --- PROCEDURE GetLastError() RETURNS string // Retorna a mensagem de erro registrada RETURN _LastError END // --- Método para configurar o timeout --- PROCEDURE SetTimeout(seconds IS int) // Define o tempo limite das requisições _Timeout = seconds END END —- Fim -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/
Registered member 3,851 messages
Posted on April, 04 2025 - 6:37 AM
Abaixo, apresento a reescrita completa da biblioteca REST em WLanguage (a linguagem usada pela plataforma WinDev da PCSoft), baseada na última versão em C++ que forneci, com todas as melhorias possíveis. Vou adaptar o código para a sintaxe e os paradigmas do WLanguage, incluindo boas práticas como tratamento de erros, suporte assíncrono (quando possível no contexto do WinDev), e extensibilidade. Vou manter os comentários em inglês para atender ao pedido. Estrutura Geral No WLanguage, usaremos Classes para representar RestCore, RestAPI, RestEndpoint, etc., e Herança para os tipos de autenticação (RestAuth e subclasses). Como o WLanguage não suporta diretamente ponteiros ou gerenciamento manual de memória como em C++, usaremos referências e gerenciamento automático de objetos. 1. Classe `RestCore` A classe principal que coordena a biblioteca. // Class: RestCore // Description: Core class to manage the REST library CLASS RestCore PUBLIC logger IS RestLogger cache IS RestCache // Constructor PROCEDURE RestCore() logger = NULL cache = NULL END // Creates an instance of RestAPI with a given configuration PROCEDURE createAPI(config IS RestAPIConfig) RETURNS RestAPI api IS RestAPI api = NEW RestAPI(config) RETURN api END // Sets a global logger PROCEDURE setLogger(newLogger IS RestLogger) logger = newLogger END // Sets a global cache PROCEDURE setCache(newCache IS RestCache) cache = newCache END END 2. Classe `RestAPI` e Estrutura `RestAPIConfig` A classe RestAPI gerencia uma API específica, e RestAPIConfig é uma estrutura para configuração. // Structure: RestAPIConfig // Description: Configuration for a REST API instance STRUCTURE RestAPIConfig baseUrl IS string auth IS RestAuth END // Class: RestAPI // Description: Manages a specific REST API instance CLASS RestAPI PUBLIC config IS RestAPIConfig // Constructor PROCEDURE RestAPI(cfg IS RestAPIConfig) config = cfg END // Creates an endpoint for a specific resource PROCEDURE createEndpoint(path IS string) RETURNS RestEndpoint endpoint IS RestEndpoint endpoint = NEW RestEndpoint(config.baseUrl, path, config.auth) RETURN endpoint END END 3. Classe `RestEndpoint` Representa um recurso específico da API e envia requisições. // Class: RestEndpoint // Description: Represents a specific API endpoint CLASS RestEndpoint PUBLIC url IS string auth IS RestAuth // Constructor PROCEDURE RestEndpoint(baseUrl IS string, path IS string, authMethod IS RestAuth) url = baseUrl + path auth = authMethod END // Sends a request and returns the response PROCEDURE sendRequest(request IS RestRequest) RETURNS RestResponse response IS RestResponse httpReq IS HTTPRequest httpReq.URL = url httpReq.Method = request.method httpReq.Content = request.body // Apply headers from request FOR EACH header IN request.headers HTTPAddHeader(httpReq, header.Key, header.Value) END // Apply authentication if present IF auth <> NULL THEN auth.applyToRequest(request) END // Execute the request IF HTTPSend(httpReq) THEN response = NEW RestResponse(httpReq.StatusCode, HTTPGetResult(httpReq)) ELSE response = NEW RestResponse(500, "Request failed: " + ErrorInfo()) END RETURN response END END 4. Classes `RestRequest` e `RestResponse` Gerenciam requisições e respostas HTTP. // Class: RestRequest // Description: Configures an HTTP request CLASS RestRequest PUBLIC method IS string body IS string headers IS associative array OF string // Constructor PROCEDURE RestRequest() method = "GET" headers = [] END // Sets the HTTP method PROCEDURE setMethod(newMethod IS string) method = newMethod END // Sets the request body PROCEDURE setBody(newBody IS string) body = newBody END // Adds a header to the request PROCEDURE addHeader(key IS string, value IS string) headers[key] = value END END // Class: RestResponse // Description: Encapsulates an HTTP response CLASS RestResponse PUBLIC statusCode IS int body IS string // Constructor PROCEDURE RestResponse(status IS int, responseBody IS string) statusCode = status body = responseBody END // Checks if the response is successful PROCEDURE isSuccess() RETURNS boolean RETURN (statusCode >= 200 AND statusCode < 300) END // Gets the status code PROCEDURE getStatusCode() RETURNS int RETURN statusCode END // Gets the response body PROCEDURE getBody() RETURNS string RETURN body END END 5. Classes de Autenticação (`RestAuth` e Subclasses) Classe base abstrata e subclasses para autenticação. // Abstract Class: RestAuth // Description: Base class for authentication methods CLASS RestAuth ABSTRACT PUBLIC PROCEDURE applyToRequest(request IS RestRequest) IS ABSTRACT END // Class: RestAuthBasic // Description: Basic authentication with username and password CLASS RestAuthBasic INHERITS RestAuth PUBLIC username IS string password IS string // Constructor PROCEDURE RestAuthBasic(user IS string, pwd IS string) username = user password = pwd END // Applies basic authentication to the request PROCEDURE applyToRequest(request IS RestRequest) creds IS string = username + ":" + password encoded IS string = Base64Encode(creds) request.addHeader("Authorization", "Basic " + encoded) END END // Class: RestAuthBearer // Description: Bearer token authentication CLASS RestAuthBearer INHERITS RestAuth PUBLIC token IS string // Constructor PROCEDURE RestAuthBearer(tkn IS string) token = tkn END // Applies bearer authentication to the request PROCEDURE applyToRequest(request IS RestRequest) request.addHeader("Authorization", "Bearer " + token) END END // Additional authentication classes can be added similarly (e.g., RestAuthApiKey, RestAuthOAuth2) 6. Funcionalidades Avançadas Exemplos de classes para funcionalidades avançadas. • RestFileTransfer: // Class: RestFileTransfer // Description: Handles file upload and download CLASS RestFileTransfer PUBLIC PROCEDURE uploadFile(endpointUrl IS string, filePath IS string) RETURNS RestResponse response IS RestResponse httpReq IS HTTPRequest httpReq.URL = endpointUrl httpReq.Method = "POST" IF HTTPAddFile(httpReq, "file", filePath) THEN IF HTTPSend(httpReq) THEN response = NEW RestResponse(httpReq.StatusCode, HTTPGetResult(httpReq)) ELSE response = NEW RestResponse(500, "Upload failed: " + ErrorInfo()) END ELSE response = NEW RestResponse(400, "Invalid file path") END RETURN response END PROCEDURE downloadFile(endpointUrl IS string, savePath IS string) RETURNS RestResponse response IS RestResponse httpReq IS HTTPRequest httpReq.URL = endpointUrl httpReq.Method = "GET" IF HTTPSend(httpReq) THEN IF HTTPGetResult(httpReq, savePath) THEN response = NEW RestResponse(httpReq.StatusCode, "File saved to " + savePath) ELSE response = NEW RestResponse(500, "Download failed: " + ErrorInfo()) END ELSE response = NEW RestResponse(500, "Request failed: " + ErrorInfo()) END RETURN response END END • RestPagination: // Class: RestPagination // Description: Manages paginated responses CLASS RestPagination PUBLIC endpoint IS RestEndpoint pageSize IS int nextUrl IS string // Constructor PROCEDURE RestPagination(ep IS RestEndpoint) endpoint = ep pageSize = 10 nextUrl = "" END // Gets the next page of results PROCEDURE getNextPage() RETURNS RestResponse request IS RestRequest request.setMethod("GET") IF nextUrl <> "" THEN endpoint.url = nextUrl END request.addHeader("Page-Size", StringBuild("%1", pageSize)) response IS RestResponse = endpoint.sendRequest(request) // Simulate parsing next URL from response (e.g., HAL or Link header) IF InString(response.getBody(), "next") > 0 THEN nextUrl = ExtractString(response.getBody(), "next=", ";") // Placeholder logic ELSE nextUrl = "" END RETURN response END // Sets the page size PROCEDURE setPageSize(size IS int) pageSize = size END END • RestRateLimit: // Class: RestRateLimit // Description: Manages rate limiting for requests CLASS RestRateLimit PUBLIC limit IS int requestsMade IS int lastReset IS DateTime // Constructor PROCEDURE RestRateLimit(requestsPerMinute IS int) limit = requestsPerMinute requestsMade = 0 lastReset = Now() END // Waits if the rate limit is reached PROCEDURE waitIfNeeded() currentTime IS DateTime = Now() IF DateTimeDifference(lastReset, currentTime, InMinutes) >= 1 THEN requestsMade = 0 lastReset = currentTime END IF requestsMade >= limit THEN Sleep((60 - DateTimeDifference(lastReset, currentTime, InSeconds)) * 1000) requestsMade = 0 lastReset = Now() END requestsMade += 1 END END 7. Exemplo de Uso Exemplo de como usar a biblioteca em WLanguage: PROCEDURE Main() core IS RestCore core = NEW RestCore() // Set a logger (optional) core.setLogger(NEW RestLogger()) // Configure the API config IS RestAPIConfig config.baseUrl = "https://api.example.com " config.auth = NEW RestAuthBasic("user", "password") // Create an API instance api IS RestAPI = core.createAPI(config) // Define an endpoint endpoint IS RestEndpoint = api.createEndpoint("/resource") // Send a GET request request IS RestRequest request.setMethod("GET") response IS RestResponse = endpoint.sendRequest(request) // Process the response IF response.isSuccess() THEN Info("Response: " + response.getBody()) ELSE Error("Error: " + StringBuild("Status %1 - %2", response.getStatusCode(), response.getBody())) END // Example with file upload fileTransfer IS RestFileTransfer fileTransfer = NEW RestFileTransfer() uploadResponse IS RestResponse = fileTransfer.uploadFile("https://api.example.com/upload ", "C:\file.txt") IF uploadResponse.isSuccess() THEN Info("File uploaded successfully") END END 8. Melhorias Incluídas 1 Suporte Assíncrono: Embora o WLanguage não tenha suporte nativo a async/await, usei HTTPSend em modo síncrono. Para assincronia, você pode integrar com threads ou eventos do WinDev. 2 Tratamento de Erros: Adicionei verificações com ErrorInfo() para capturar falhas. 3 Extensibilidade: A estrutura permite adicionar novos tipos de autenticação ou funcionalidades facilmente. 4 Configuração Flexível: A estrutura RestAPIConfig pode ser expandida com mais opções (timeout, headers padrão, etc.). 5 Funcionalidades Avançadas: Incluí suporte a upload/download, paginação e limite de taxa, com lógica adaptada ao WLanguage. Essa implementação em WLanguage reflete todos os requisitos e melhorias discutidos, aproveitando os recursos da linguagem para criar uma biblioteca REST robusta e prática. Se precisar de ajustes específicos (como suporte a WebSockets ou HAL), posso expandir ainda mais! -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/