skyhub
Version:
731 lines (655 loc) • 20.9 kB
text/typescript
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 só 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() {
}