UNPKG

skyhub

Version:
731 lines (655 loc) 20.9 kB
import Axios, { AxiosRequestConfig, Method } from 'axios'; import { EventEmitter } from 'eventemitter3' import { sleep } from './sleep'; export namespace SkyHub { const BASE_URL = 'https://api.skyhub.com.br'; export interface Parameters { /** User email for 'X-User-Email' header */ userEmail: string, /** Api Key or token for 'X-Api-Key' header */ apiKey: string, /** Account Manager Key for 'X-Accountmanager-Key' header */ accountManagerKey: string } export interface IResult<T> { success: boolean, response: T, httpStatus: number, statusText: string, elapsed: number } export interface IGetPedidos { total: number orders: Array<Order> } export class Address { /** Exemplo: "Nome do comprador"*/ full_name: string /** Exemplo: "Rua de teste"*/ street: string /** Exemplo: 1234*/ number: number /** Exemplo: "Ponto de referência teste"*/ detail: string /** Exemplo: "Bairro teste"*/ neighborhood: string /** Exemplo: "Cidade de teste"*/ city: string /** Exemplo: "UF"*/ region: string /** Exemplo: "BR"*/ country: string /** Exemplo: "90000000"*/ postcode: string } export class OrderPayment { } export class OrderItem { /** Exemplo: "teste002-azul"*/ id: string /** Exemplo: "teste002"*/ product_id: string /** Exemplo: "Produto de teste com variação"*/ name: string /** Exemplo: 1*/ qty: number /** Exemplo: 0.85*/ original_price: number /** Exemplo: 0.85*/ special_price: number } export class Order { /** Exemplo: "Marketplace-000000002" */ code: string /** Exemplo: "Marketplace" */ channel: string /** Exemplo: "2015-01-01T10:10:00-03:00" */ placed_at: string /** Exemplo: "2015-01-01T10:10:00-03:00" */ updated_at: string /** Exemplo: 5.85 */ total_ordered: number /** Exemplo: 0 */ interest: number /** Exemplo: 5 */ shipping_cost: number /** Exemplo: "Transportadora" */ shipping_method: string /** Exemplo: "2015-01-10T10:10:10-03:00" */ estimated_delivery: string /** Exemplo: null */ estimated_delivery_shift: object /** Exemplo: { "full_name": "Nome do recebedor", "street": "Rua de teste", "number": 1234, "detail": "Ponto de referência teste", "neighborhood": "Bairro teste", "city": "Cidade de teste", "region": "UF", "country": "BR", "postcode": "90000000" } */ shipping_address: Address /** Exemplo: { "full_name": "Nome do comprador", "street": "Rua de teste", "number": 1234, "detail": "Ponto de referência teste", "neighborhood": "Bairro teste", "city": "Cidade de teste", "region": "UF", "country": "BR", "postcode": "90000000" } */ billing_address: Address /** Exemplo: { "name": "Nome do comprador", "email": "comprador@exemplo.com.br", "date_of_birth": null, "gender": "", "vat_number": 12312312309, "phones": ["8899999999"] } */ customer: Customer /** Exemplo: [{ "id": "teste002-azul", "product_id": "teste002", "name": "Produto de teste com variação", "qty": 1, "original_price": 0.85, "special_price": 0.85 }] */ items: Array<OrderItem> /** Exemplo: { "code": "payment_received", "label": "Pagamento aprovado", "type": "APPROVED" } */ status: OrderStatus /** Exemplo: "NOT_SYNCED" ou "SYNCED" */ sync_status: string | 'SYNCED' | 'NOT_SYNCED' /** Exemplo: [] */ invoices: Array<OrderInvoice> /** Exemplo: [] */ shipments: Array<OrderShipment> /** Exemplo: [] */ payments: Array<OrderPayment> } /** Interface para definição de atributos como: Tamanho, Cor, Crossdocking, etc * Exemplo: { "key": "Tamanho", "value: "M" } * */ export class Specification { /** Exemplo: Tamanho */ key: string /** Exemplo: M */ value: string } /** Interface para definição de categorias * Exemplo: { "code": "01", "name": "Blusa" } * Exemplo Sub-categoria: { "code": "02", "name": "Blusa > Manga Curta" } * */ export class Category { /** Exemplo: "01" */ code: string /** Exemplo: "skyhub homologação" */ name: string } export class SimpleProduct { /** Exemplo: "200" */ sku: string /** Exemplo: "PRODUTO SIMPLES" */ name: string /** Exemplo: "PRODUTO SIMPLES" */ description: string /** Exemplo: "enable" */ status: string /** Exemplo: 0 */ qty: number /** Exemplo: 100 */ price: number /** Exemplo: 89.99 */ promotional_price: number /** Exemplo: 49 */ cost: number /** Exemplo: 3 */ weight: number /** Exemplo: 1 */ height: number /** Exemplo: 1 */ width: number /** Exemplo: 1 */ length: number /** Exemplo: "SKYHUB" */ brand: string /** Exemplo: "" */ ean: string /** Exemplo: "" */ nbm: string /** Exemplo: [{ "code": "01", "name": "skyhub homologação" }] */ categories: Array<Category> /** Exemplo: [""] */ images: Array<string> /** Exemplo: [{ "key": "Tamanho", "value": "M" }] */ specifications: Array<Specification> } export class Variation { /** Exemplo: "COD_SKU_VARIACAO" */ sku: string /** Exemplo: 10 */ qty: number /** Exemplo: "9876543210987" */ ean: string /** Exemplo: ["http://d26lpennugtm8s.cloudfront.net/stores/154/284/products/camiseta-lisa-verde-bandeira-algodo-p-ao-gg-pronta-entrega-355901-mlb20431777049_092015-o-07fadec89e5ed54705c1b9ab5411dec8-1024-1024.jpg"] */ images: Array<string> /** Exemplo: [{ "key": "Cor", "value": "Verde" }, { "key": "Tamanho", "value": "M" }] */ specifications: Array<Specification> } export class ProductWithVariation { /** Exemplo: "CODIGO_SKU" */ sku: string /** Exemplo: "DESCRICAO PRODUTO" */ name: string /** Exemplo: "CRIAR PRODUTO COM UMA VARIAÇÃO" */ description: string /** Exemplo: "enabled" */ status: string /** Exemplo: 30 */ price: number /** Exemplo: 29.9 */ promotional_price: number /** Exemplo: 0 */ cost: number /** Exemplo: 0.1 */ weight: number /** Exemplo: 20 */ height: number /** Exemplo: 30 */ width: number /** Exemplo: 20 */ length: number /** Exemplo: "Marca" */ brand: string /** Exemplo: "98769898" */ nbm: string /** Exemplo: [{ "code": "01", "name": "SKYHUB HOMOLOGAÇÃO" }] */ categories: Array<Category> /** Exemplo: ["http://d26lpennugtm8s.cloudfront.net/stores/154/284/products/camiseta-lisa-verde-bandeira-algodo-p-ao-gg-pronta-entrega-355901-mlb20431777049_092015-o-07fadec89e5ed54705c1b9ab5411dec8-1024-1024.jpg"] */ images: Array<string> /** Exemplo: [{ "key": "Especicações do Produto PAI", "value": "Especificações do Produto PAI" }] */ specifications: Array<Specification> /** Exemplo: [{ "sku": "COD_SKU_VARIACAO", "qty": 10, "ean": "9876543210987", "images": ["http://d26lpennugtm8s.cloudfront.net/stores/154/284/products/camiseta-lisa-verde-bandeira-algodo-p-ao-gg-pronta-entrega-355901-mlb20431777049_092015-o-07fadec89e5ed54705c1b9ab5411dec8-1024-1024.jpg"], "specifications": [{ "key": "Cor", "value": "Verde" }, { "key": "Tamanho", "value": "M" }] }] */ variations: Array<Variation> /** Exemplo: ["Cor", "Tamanho"] */ variation_attributes: Array<string> } export class OrderShipment { /** Exemplo: "Submarino-1493069158776" */ code: string /** Exemplo: [{ "sku": "1001", "qty": 1 }] */ items: Array<OrderShipmentItem> /** Exemplo: { "code": "BR1321830198302DR", "carrier": "Correios", "method": "SEDEX", "url": "www.correios.com.br" } */ track: OrderShipmentTrack } export class OrderShipmentItem { /** Exemplo: "1001" */ sku: string /** Exemplo: 1 */ qty: number } export class OrderShipmentTrack { /** Exemplo: "BR1321830198302DR" */ code: string /** Exemplo: "Correios" */ carrier: string /** Exemplo: "SEDEX" */ method: string /** Exemplo: "www.correios.com.br" */ url: string } /* * Exemplo: { "code": "payment_received", "label": "Pagamento aprovado", "type": "APPROVED" }*/ export class OrderStatus { /** Exemplo: "payment_received"*/ code: string /** Exemplo: "Pagamento aprovado"*/ label: string /** Exemplo: "APPROVED"*/ type: string } export class OrderInvoice { /** Exemplo: "99999999999999999999999999999999999999999999" */ key: string } export class Customer { /** Exemplo: "Nome do comprador" */ name: string /** Exemplo: "comprador@exemplo.com.br" */ email: string /** Exemplo: null */ date_of_birth: string /** Exemplo: "" */ gender: string /** Exemplo: 12312312309 */ vat_number: number /** Exemplo: ["8899999999"] */ phones: Array<string> } export interface Headers { 'X-User-Email': string 'X-Api-Key': string 'X-Accountmanager-Key': string 'Accept': 'application/json;charset=UTF-8' 'Content-Type': 'application/json' } class RestClient { headers: any; baseUrl: string; private options(data: any, params: any = undefined): AxiosRequestConfig { /* { adapter: AxiosAdapter, auth: { username: '', password: '' }, baseURL: '', cancelToken: CancelToken, data: any, headers: any, httpAgent: any, httpsAgent:any, maxContentLength: string, maxRedirects: number, method: string, onDownloadProgress: (progressEvent) => void, onUploadProgress: (progressEvent) => void, params: any, paramsSerializer: string, proxy: AxiosProxyConfig, responseType: string, timeout: number, transformRequest: AxiosTransformer | AxiosTransformer[], transformResponse: AxiosTransformer | AxiosTransformer[], url: string, validateStatus: boolean, withCredentials: boolean, xsrfCookieName: string, xsrfHeaderName: string } */ return { headers: this.headers, data: data, params: params }; } private full(api) { let ew = `${this.baseUrl}`.indexOf('/') === `${this.baseUrl}`.length - 1; let sw = `${api}`.indexOf('/') === 0; if (ew && sw) return `${this.baseUrl}${api.substr(1)}`; if ((!ew && sw) || (ew && !sw)) return `${this.baseUrl}${api}`; return `${this.baseUrl}/${api}`; } get<T>(api, params, data = null) { return Axios.get<T>(this.full(api), this.options(params, data)); } post<T>(api, data, params = undefined) { return Axios.post<T>(this.full(api), data, this.options(data, params)) } put(api, data) { return Axios.put(this.full(api), data, this.options(data)); } delete(api, data) { return Axios.delete(this.full(api), this.options(data)); } head<T>(api, data) { return Axios.head(this.full(api), this.options(data)); } translateMethod(method): Method { return method; } custom<T>(method, api, data, params) { let o = this.options(data, params); o.url = api; o.method = this.translateMethod(`${method}`.toLowerCase()); return Axios.request<T>(o); } constructor(baseUrl: string, headers: any) { this.baseUrl = baseUrl; this.headers = headers; } } interface IClient { on(event: 'status-429', listener: Function); } export class Client extends EventEmitter implements IClient { private headers: Headers = { "Content-Type": "application/json", Accept: "application/json;charset=UTF-8", "X-Accountmanager-Key": null, "X-Api-Key": null, "X-User-Email": null }; private client: RestClient; private lastResponse: any; private async post(api: string, data: any) { var res; var start = Date.now(); try { res = this.lastResponse = await this.client.post(api, data); if (res.status === 201) return { success: true, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } catch (e) { res = await e.response; if (res.status === 429) { this.emit('status-429'); await sleep(1000); return await this.post(api, data); } } return { success: false, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } private async get(api: string, data: any = null) { var res; var start = Date.now(); try { res = this.lastResponse = await this.client.get(api, data); if (res.status === 200) return { success: true, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } catch (e) { res = await e.response; if (res.status === 429) { this.emit('status-429'); await sleep(1000); return await this.get(api, data); } } return { success: false, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } /** * * @param {string} api * @param {any} data * @returns {{ success: boolean, response: any, httpStatus: number, statusText: string, elapsed: number }} */ private async put(api, data) { var res; var start = Date.now(); try { res = this.lastResponse = await this.client.put(api, data); if (res.status === 204) return { success: true, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } catch (e) { res = await e.response; if (res.status === 429) { this.emit('status-429'); await sleep(1000); return await this.put(api, data); } } return { success: false, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } /** * * @param {string} api * @param {any} data * @returns {{ success: boolean, response: any, httpStatus: number, statusText: string, elapsed: number }} */ private async delete(api, data = {}) { var res; var start = Date.now(); try { res = this.lastResponse = await this.client.delete(api, data); if (res.status === 204) return { success: true, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } catch (e) { res = await e.response; if (res.status === 429) { this.emit('status-429'); await sleep(1000); return await this.delete(api, data); } } return { success: false, response: res.data, httpStatus: res.status, statusText: res.statusText, elapsed: Date.now() - start }; } constructor(parameters: SkyHub.Parameters) { super(); if (typeof parameters === 'undefined') throw new Error(`Missing initialization 'parameters'`); if (typeof parameters !== 'object' || Array.isArray(parameters)) throw new Error('Initialization parameters must be an object'); if (!parameters.userEmail) throw new Error(`Missing 'userEmail' on initialization parameters`); if (!parameters.apiKey) throw new Error(`Missing 'apiKey' on initialization parameters`); if (!parameters.accountManagerKey) throw new Error(`Missing 'accountManagerKey' on initialization parameters`); this.headers["X-User-Email"] = parameters.userEmail; this.headers["X-Api-Key"] = parameters.apiKey; this.headers["X-Accountmanager-Key"] = parameters.accountManagerKey; this.client = new RestClient(BASE_URL, this.headers); } /** * Max Products Returned Per Page: 100 * @param page (min: 1) * @param per_page (max: 100) */ async getProdutos(page: number = 1, per_page: number = 100): Promise<IResult<any>> { return this.get('/products', { page: page, per_page: per_page }); } /** * Lista os produtos cadastrados agrupados por página (conforme parametro 'per_page', suportado no máx 100) * NOTA: Não será possível listar mais que 10.000 (dez mil) itens */ async getProduto(codigo_sku: string) { return this.get(`/products/${codigo_sku}`); } /** * Cadastra um novo Produto */ async postProduto(product: SkyHub.SimpleProduct | SkyHub.ProductWithVariation) { return this.post('/products', JSON.stringify({ product: product })); } /** * Atualiza Cadastro do Produto * Nota: Atualiza apenas campos informados no parametro produto * @param {Produto} produto Campos informados serão atualizados, campos não informados serão mantidos os valores atuais * @returns {SkyHub.IResult<SkyHub.PutProduto>} */ async putProduto(sku: string, produto: SkyHub.SimpleProduct | SkyHub.ProductWithVariation) { return this.put(`/products/${encodeURIComponent(sku)}`, JSON.stringify({ product: produto })); } /** * Remove um Produto */ async deleteProduto(sku: string) { return this.delete(`/products/${encodeURIComponent(sku)}`); } /** Retorna 1 Pedido da fila * */ async getPedidoNaFila() { return this.get('/queues/orders'); } /** Retorna lista de pedidos */ async getPedidos() { return this.get('/orders'); } /** * Retorna 1 único pedido */ async getPedido(id_pedido: string) { return this.get('/orders/' + encodeURIComponent(id_pedido)); } /** * Confirma o Processamento (gravação no ERP) do pedido, removendo-o da fila de pedidos do SkyHub */ async confirmarProcessamentoPedido(id_pedido_skyhub: string) { return this.delete(`/queues/orders/${encodeURIComponent(id_pedido_skyhub)}`) } /** * Altera Status do Pedido para 'Aprovado' no SkyHub * NOTA: ESTA CHAMADA NÃO ESTÁ DISPONÍVEL EM PRODUÇÃO! * @param {string} id_pedido_skyhub * @returns {Promise<SkyHub.IResult<SkyHub.ISetPedidoAprovado>>} */ async setPedidoAprovado(id_pedido_skyhub: string) { return this.post(`/orders/${encodeURIComponent(id_pedido_skyhub)}/approval`, JSON.stringify({ status: 'payment_received', })); } /** * Altera Status do Pedido para 'Faturado' no SkyHub, vinculando a chave de acesso * @param {string} id_pedido_skyhub * @param {string} chaveAcesso * @returns {Promise<SkyHub.IResult<SkyHub.ISetPedidoFaturado>>} */ async setPedidoFaturado(id_pedido_skyhub: string, chaveAcesso: string) { return this.post(`/orders/${encodeURIComponent(id_pedido_skyhub)}/invoice`, JSON.stringify({ status: 'payment_received', invoice: { key: chaveAcesso } })); } /** * Altera Status do Pedido para 'Não Entregue' no SkyHub */ async setPedidoNaoEntregue(id_pedido_skyhub: string, obs: string) { let d = new Date(); let tz = d.toString().replace(/.+([+-]\d\d)\:?(\d\d).+/, '$1:$2'); /*let tzMinutes = Math.abs(d.getTimezoneOffset() % 60).toString().padStart(2, '0'); let tzHours = ((d.getTimezoneOffset() - Math.abs(d.getTimezoneOffset() % 60)) / 60); tzHours = (tzHours < 0 ? `+` : `-`) + tzHours.toString().padStart(2, '0');*/ let dados = { shipment_exception: { occurrence_date: `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}T${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}:${d.getSeconds().toString().padStart(2, '0')}${tz}`, observation: obs } }; return this.post(`/orders/${encodeURIComponent(id_pedido_skyhub)}/shipment_exception`, JSON.stringify(dados)); } /** * Altera Status do Pedido para 'Entregue a Transportadora' no SkyHub, vinculando dados da Transportadora */ async setPedidoEntregueTransportadora(id_pedido_skyhub: string, shipment: SkyHub.OrderShipment) { return this.post(`/orders/${encodeURIComponent(id_pedido_skyhub)}/shipments`, JSON.stringify({ status: 'order_shipped', shipment: shipment })); } /** * Altera Status do Pedido para 'Entregue ao Cliente' no SkyHub */ async setPedidoEntregueCliente(id_pedido_skyhub: string) { return this.post(`/orders/${encodeURIComponent(id_pedido_skyhub)}/delivery`, JSON.stringify({ status: 'complete' })); } /** * Altera Status do Pedido para 'Cancelado' no SkyHub * @param id_pedido_skyhub */ async setPedidoCancelado(id_pedido_skyhub: string) { return this.post(`/orders/${encodeURIComponent(id_pedido_skyhub)}/cancel`, JSON.stringify({ status: 'order_canceled' })); } /** * Cria um novo pedido no SkyHub * IMPORTANTE: Esta chamada funciona em homologação * @param {SkyHub.NewOrder} order * @returns {Promise<SkyHub.IResult<SkyHub.INovoPedidoTeste>>} */ async novoPedidoTeste(order) { return this.post('/orders', JSON.stringify({ order: order })); } } } export default SkyHub.Client; function myfunction() { }