<|start_filename|>lib/resources/versions/vendor_direct_fulfillment_payments/vendorDirectFulfillmentPayments_v1.js<|end_filename|>
module.exports = {
  'v1':{
    submitInvoice:(req_params) => {
      return Object.assign(req_params, {
        method:'POST',
        api_path:'/vendor/directFulfillment/payments/v1/invoices',
        restore_rate:0.1
      });
    }
  }
};

<|start_filename|>lib/resources/vendorInvoices.js<|end_filename|>
module.exports = {
  vendorInvoices:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'submitInvoices'
  	],
    ...require('./versions/vendor_invoices/vendorInvoices_v1')
  }
};

<|start_filename|>lib/resources/sales.js<|end_filename|>
module.exports = {
   sales:{
    __versions:[
      'v1'
    ],
    __operations:[
      'getOrderMetrics'
    ],
    ...require('./versions/sales/sales_v1')
  }
};

<|start_filename|>test/specs/operations/listingsRestrictions.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'listingsRestrictions';

describe(endpoint, async function(){

	it('should return an array of restrictions', async function(){
    if (this.config.seller_id){
  		let res = await this.sellingPartner.callAPI({
  			operation:'getListingsRestrictions',
        endpoint:endpoint,
        query:{
          sellerId:this.config.seller_id,
          asin:this.config.asin,
          marketplaceIds:this.config.marketplace_id
        }
      });
      expect(res).to.be.a('object');
      expect(res.restrictions).to.be.a('array');
    } else {
      this.skip();
    }
	});
  
});

<|start_filename|>lib/resources/fulfillmentOutbound.js<|end_filename|>
module.exports = {
   fulfillmentOutbound:{
    __versions:[
      '2020-07-01'
    ],
    __operations:[
      'getFulfillmentPreview',
      'listAllFulfillmentOrders',
      'createFulfillmentOrder',
      'getPackageTrackingDetails',
      'listReturnReasonCodes',
      'createFulfillmentReturn',
      'getFulfillmentOrder',
      'updateFulfillmentOrder',
      'cancelFulfillmentOrder',
      'getFeatures',
      'getFeatureInventory',
      'getFeatureSKU'
    ],
    ...require('./versions/fulfillment_outbound/fulfillmentOutbound_2020-07-01')
  }
};

<|start_filename|>lib/resources/fulfillmentInbound.js<|end_filename|>
module.exports = {
   fulfillmentInbound:{
    __versions:[
      'v0'
    ],
    __operations:[
      'getInboundGuidance',
      'createInboundShipmentPlan',
      'updateInboundShipment',
      'createInboundShipment',
      'getPreorderInfo',
      'confirmPreorder',
      'getPrepInstructions',
      'getTransportDetails',
      'putTransportDetails',
      'voidTransport',
      'estimateTransport',
      'confirmTransport',
      'getLabels',
      'getBillOfLading',
      'getShipments',
      'getShipmentItemsByShipmentId',
      'getShipmentItems'
    ],
    ...require('./versions/fulfillment_inbound/fulfillmentInbound_v0')
  }
};

<|start_filename|>lib/resources/vendorOrders.js<|end_filename|>
module.exports = {
  vendorOrders:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'getPurchaseOrders',
			'getPurchaseOrder',
			'submitAcknowledgement',
			'getPurchaseOrdersStatus'
  	],
    ...require('./versions/vendor_orders/vendorOrders_v1')
  }
};

<|start_filename|>lib/endpoints.js<|end_filename|>
// For a list of required and optional body, query or path parameters for resources please see official references:
// --> https://github.com/amzn/selling-partner-api-docs/tree/main/references

const {readdirSync} = require('fs');
const path = require('path');

module.exports = {
	...readdirSync(__dirname + '/resources').reduce((eps, ep) => {
		if (path.extname(ep) === '.js'){
			return Object.assign(eps, {
				...require('./resources/' + ep)
			});
		}
		return eps;
	}, {})
};

<|start_filename|>lib/resources/vendorDirectFulfillmentOrders.js<|end_filename|>
module.exports = {
  vendorDirectFulfillmentOrders:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'getOrders',
    	'getOrder',
    	'submitAcknowledgement'
  	],
    ...require('./versions/vendor_direct_fulfillment_orders/vendorDirectFulfillmentOrders_v1')
  }
};

<|start_filename|>index.js<|end_filename|>
module.exports = require('./lib/SellingPartner');

<|start_filename|>lib/resources/catalogItems.js<|end_filename|>
module.exports = {
  catalogItems:{
    __versions:[
    	'v0',
    	'2020-12-01'
  	],
    __operations:[
    	'listCatalogItems',
    	'getCatalogItem',
    	'listCatalogCategories',
      'searchCatalogItems'
  	],
    ...require('./versions/catalog_items/catalogItems_v0'),
    ...require('./versions/catalog_items/catalogItems_2020-12-01')
  }
};

<|start_filename|>lib/resources/shipping.js<|end_filename|>
module.exports = {
   shipping:{
    __versions:[
      'v1'
    ],
    __operations:[
      'createShipment',
      'getShipment',
      'cancelShipment',
      'purchaseLabels',
      'retrieveShippingLabel',
      'purchaseShipment',
      'getRates',
      'getAccount',
      'getTrackingInformation'
    ],
    ...require('./versions/shipping/shipping_v1')
  }
};

<|start_filename|>lib/resources/merchantFulfillment.js<|end_filename|>
module.exports = {
   merchantFulfillment:{
    __versions:[
      'v0'
    ],
    __operations:[
      'getEligibleShipmentServicesOld',
      'getEligibleShipmentServices',
      'getShipment',
      'cancelShipment',
      'cancelShipmentOld',
      'createShipment',
      'getAdditionalSellerInputsOld',
      'getAdditionalSellerInputs'
    ],
    ...require('./versions/merchant_fulfillment/merchantFulfillment_v0')
  }
};

<|start_filename|>test/specs/operations/notifications.spec.js<|end_filename|>
const SellingPartnerAPI = require('../../../index');
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'notifications';

describe(endpoint, async function(){

	let subscription_id;
	let destination_id;
	let sellingPartner;

	it('should return subscriptions for any offer changed', async function(){
		sellingPartner = new SellingPartnerAPI({
	    region:this.config.region,
	    refresh_token:this.config.refresh_token,
	    access_token:this.config.access_token,
	    role_credentials:this.config.role_credentials,
	    options:{
	      auto_request_tokens:false
	    }
	  });
		try {
			let res = await sellingPartner.callAPI({
				operation:'getSubscription',
				endpoint:endpoint,
				path:{
					notificationType:'ANY_OFFER_CHANGED'
				}
	    });
	    expect(res).to.be.a('object');
	    expect(res.subscriptionId).to.be.a('string');
	    expect(res.payloadVersion).to.be.a('string');
	    expect(res.destinationId).to.be.a('string');
	    if (res.subscriptionId){
	    	subscription_id = res.subscription_id;
	    	destination_id = res.destination_id;
	    }
	  } catch(e){
	  	expect(e).to.have.property('details');
      expect(e.details).to.include('Subscription doesn\'t exist for notification type');
	  }
	});

	it('should return subscriptions for subscription id', async function(){
		if (subscription_id){
			try {
				await sellingPartner.refreshAccessToken('sellingpartnerapi::notifications');
				let res = await sellingPartner.callAPI({
					operation:'getSubscriptionById',
					endpoint:endpoint,
					path:{
						subscriptionId:subscription_id,
						notificationType:'ANY_OFFER_CHANGED'
					}
		    });
		    expect(res).to.be.a('object');
		    expect(res.subscriptionId).to.be.a('string');
		    expect(res.payloadVersion).to.be.a('string');
		    expect(res.destinationId).to.be.a('string');
		  } catch(e){
		  	expect(e).to.have.property('details');
	      expect(e.details).to.include('Subscription doesn\'t exist for notification type');
		  }
		} else {
			this.skip();
		}
	});

	it('should return destinations', async function(){
		await sellingPartner.refreshAccessToken('sellingpartnerapi::notifications');
		let res = await sellingPartner.callAPI({
			operation:'getDestinations',
			endpoint:endpoint,
    });
    expect(res).to.be.a('array');
    if (res.length && res[0].destinationId){
    	destination_id = res[0].destination_id;
    }
	});

	it('should return destination for destination id', async function(){
		if (destination_id){
			try {
				await sellingPartner.refreshAccessToken('sellingpartnerapi::notifications');
				let res = await sellingPartner.callAPI({
					operation:'getDestination',
					endpoint:endpoint,
					path:{
						destinationId:destination_id
					}
		    });
		    expect(res).to.be.a('object');
		    expect(res.subscriptionId).to.be.a('string');
		    expect(res.payloadVersion).to.be.a('string');
		    expect(res.destinationId).to.be.a('string');
		  } catch(e){
		  	expect(e).to.have.property('details');
	      expect(e.details).to.include('Subscription doesn\'t exist for notification type');
		  }
		} else {
			this.skip();
		}
	});

});

<|start_filename|>lib/resources/messaging.js<|end_filename|>
module.exports = {
   messaging:{
    __versions:[
      'v1'
    ],
    __operations:[
      'getMessagingActionsForOrder',
      'confirmCustomizationDetails',
      'createConfirmDeliveryDetails',
      'createLegalDisclosure',
      'createNegativeFeedbackRemoval',
      'createConfirmOrderDetails',
      'createConfirmServiceDetails',
      'CreateAmazonMotors',
      'CreateWarranty',
      'GetAttributes',
      'createDigitalAccessKey',
      'createUnexpectedProblem'
    ],
    ...require('./versions/messaging/messaging_v1')
  }
};

<|start_filename|>lib/resources/reports.js<|end_filename|>
module.exports = {
   reports:{
    __versions:[
      '2020-09-04',
      '2021-06-30'
    ],
    __operations:[
      'getReports',
      'createReport',
      'getReport',
      'cancelReport',
      'getReportSchedules',
      'createReportSchedule',
      'getReportSchedule',
      'cancelReportSchedule',
      'getReportDocument'
    ],
    ...require('./versions/reports/reports_2020-09-04'),
    ...require('./versions/reports/reports_2021-06-30')
  }
};

<|start_filename|>lib/resources/listingsRestrictions.js<|end_filename|>
module.exports = {
 	listingsRestrictions:{
    __versions:[
    	'2021-08-01'
  	],
    __operations:[
    	'getListingsRestrictions'
  	],
    ...require('./versions/listings_restrictions/listingsRestrictions_2021-08-01')
  }
};

<|start_filename|>test/hooks.js<|end_filename|>
const config = require('./config');
const SellingPartnerAPI = require('../index');

module.exports.mochaHooks = {

	beforeAll:async function(){	
    let sellingPartner = new SellingPartnerAPI({
      region:config.region,
      refresh_token:config.refresh_token,
      options:{
        auto_request_tokens:false
      }
    });
    await sellingPartner.refreshAccessToken();
    await sellingPartner.refreshRoleCredentials();
    config.access_token = sellingPartner.access_token;
    config.role_credentials = sellingPartner.role_credentials;
  	this.config = config;
  },

  beforeEach:function(done){
    let skip_hook_titles = ['configErrors', 'authorization', 'notifications'];
    if (!skip_hook_titles.includes(this.currentTest.parent.title)){
      this.sellingPartner = new SellingPartnerAPI({
        region:config.region,
        refresh_token:config.refresh_token,
        access_token:config.access_token,
        role_credentials:config.role_credentials,
        options:{
          auto_request_tokens:false
        }
      });
    }
    done();
  }

};

<|start_filename|>lib/resources/authorization.js<|end_filename|>
module.exports = {
  authorization:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'getAuthorizationCode',
  	],
    ...require('./versions/authorization/authorization_v1')
  }
};

<|start_filename|>lib/resources/shipmentInvoicing.js<|end_filename|>
module.exports = {
	// shipmentInvoicing endpoint is available only for the Brazil marketplace!
  shipmentInvoicing:{
    __versions:[
    	'v0'
    ],
    __operations:[
	    'getShipmentDetails',
	    'submitInvoice',
	    'getInvoiceStatus'
    ],
    ...require('./versions/shipment_invoicing/shipmentInvoicing_v0')
  }
};

<|start_filename|>lib/resources/vendorDirectFulfillmentInventory.js<|end_filename|>
module.exports = {
  vendorDirectFulfillmentInventory:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'submitInventoryUpdate'
  	],
    ...require('./versions/vendor_direct_fulfillment_inventory/vendorDirectFulfillmentInventory_v1')
  }
};

<|start_filename|>lib/CustomError.js<|end_filename|>
class CustomError extends Error {

  constructor(err, ...params) {
    super(...params);

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, CustomError);
    }

    for (let key in err) {
      if (key === 'stacktrace') {
        this['original_stacktrace'] = err[key];
      } else {
        this[key] = err[key];
      }
    }
    this.type = err.type ? err.type : 'error';
  }
  
}

module.exports = CustomError;

<|start_filename|>lib/resources/tokens.js<|end_filename|>
module.exports = {
  tokens:{
    __versions:[
    	'2021-03-01'
  	],
    __operations:[
    	'createRestrictedDataToken'
  	],
    ...require('./versions/tokens/tokens_2021-03-01')
  }
};

<|start_filename|>lib/resources/fbaSmallAndLight.js<|end_filename|>
module.exports = {
   fbaSmallAndLight:{
    __versions:[
      'v1'
    ],
    __operations:[
      'getSmallAndLightEnrollmentBySellerSKU',
      'putSmallAndLightEnrollmentBySellerSKU',
      'deleteSmallAndLightEnrollmentBySellerSKU',
      'getSmallAndLightEligibilityBySellerSKU',
      'getSmallAndLightFeePreview'
    ],
    ...require('./versions/fba_small_and_light/fbaSmallAndLight_v1')
  }
};

<|start_filename|>lib/resources/solicitations.js<|end_filename|>
module.exports = {
   solicitations:{
    __versions:[
      'v1'
    ],
    __operations:[
      'getSolicitationActionsForOrder',
      'createProductReviewAndSellerFeedbackSolicitation'
    ],
    ...require('./versions/solicitations/solicitations_v1')
  }
};

<|start_filename|>lib/resources/vendorDirectFulfillmentShipping.js<|end_filename|>
module.exports = {
  vendorDirectFulfillmentShipping:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'getShippingLabels',
			'submitShippingLabelRequest',
			'getShippingLabel',
			'submitShipmentConfirmations',
			'submitShipmentStatusUpdates',
			'getCustomerInvoices',
			'getCustomerInvoice',
			'getPackingSlips',
			'getPackingSlip'
  	],
    ...require('./versions/vendor_direct_fulfillment_shipping/vendorDirectFulfillmentShipping_v1')
  }
};

<|start_filename|>lib/resources/notifications.js<|end_filename|>
module.exports = {
   notifications:{
    __versions:[
      'v1'
    ],
    __operations:[
      'getSubscription',
      'createSubscription',
      'getSubscriptionById',
      'deleteSubscriptionById',
      'getDestinations',
      'createDestination',
      'getDestination',
      'deleteDestination'
    ],
    ...require('./versions/notifications/notifications_v1')
  }
};

<|start_filename|>test/specs/operations/tokens.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'tokens';

describe(endpoint, async function(){

  it('should return a restricted data token or an access denied error', async function(){
    try {
      let res = await this.sellingPartner.callAPI({
        operation:'createRestrictedDataToken',
        endpoint:endpoint,
        body:{
          restrictedResources:[{
            path:'/orders/v0/orders',
            method:'GET',
            dataElements:['buyerInfo', 'shippingAddress']
          }]
        }
      });
      expect(res).to.be.a('object');
      expect(res.restrictedDataToken).to.be.a('string');
      expect(res.expiresIn).to.be.a('number');
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.message).to.equal('Application does not have access to one or more requested data elements: [buyerInfo, shippingAddress]');
    }
  });

});

<|start_filename|>test/specs/operations/sellers.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'sellers';

describe(endpoint, async function(){

	it('should return list of marketplaces the seller can sell in', async function(){
		let res = await this.sellingPartner.callAPI({
			operation:'getMarketplaceParticipations',
      endpoint:endpoint
    });
    expect(res).to.be.a('array');
	});

	it('should return list of marketplaces the seller can sell in as raw result', async function(){
		let res = await this.sellingPartner.callAPI({
			operation:'getMarketplaceParticipations',
      endpoint:endpoint,
			options:{
    		raw_result:true
    	}
    });
    expect(res).to.be.a('object');
    expect(res.body).to.be.a('string');
    expect(res.chunks).to.be.a('array');
    expect(res.statusCode).to.equal(200);
    expect(res.headers).to.be.a('object');
	});

});

<|start_filename|>lib/request.js<|end_filename|>
const https = require('https');
const { URL } = require('url');

module.exports = (req_options) => {
  return new Promise((resolve, reject) => {
    let url = new URL(req_options.url);
    let options = {
      method:req_options.method,
      port:443,
      hostname:url.hostname,
      path:url.pathname + url.search,
      headers:req_options.headers || {}
    };
    let post_params;
    if (req_options.body){
      post_params = req_options.body;
      options.headers['Content-Length'] = Buffer.byteLength(post_params);
    }
    let req = https.request(options, (res) => {
      let chunks = [];
      let body = '';
      res.on('data', (chunk) => {
        body += chunk;
        chunks.push(chunk);
      });
      res.on('end', () => {
        resolve({
          body:body,
          chunks:chunks,
          statusCode:res.statusCode,
          headers:res.headers
        });
      });
    });

    req.on('error', (e) => {
      reject(e);
    });
    if (post_params){
      req.write(post_params, 'utf8');
    }
    req.end();
  });
};


<|start_filename|>lib/resources/versions/listings_restrictions/listingsRestrictions_2021-08-01.js<|end_filename|>
module.exports = {
  '2021-08-01':{
    getListingsRestrictions:(req_params) => {
      return Object.assign(req_params, {
        method:'GET',
        api_path:'/listings/2021-08-01/restrictions',
        restore_rate:0.2
      });
    }
  }
};

<|start_filename|>test/specs/operations/authorization.spec.js<|end_filename|>
const SellingPartnerAPI = require('../../../index');
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'authorization';

describe(endpoint, async function(){

	it('should throw an invalid party id error', async function(){
    let sellingPartner = new SellingPartnerAPI({
      region:this.config.region,
      options:{
        auto_request_tokens:false,
        only_grantless_operations:true
      }
    });

    await sellingPartner.refreshAccessToken('sellingpartnerapi::migration');
    await sellingPartner.refreshRoleCredentials();

    try {
  		let res = await sellingPartner.callAPI({
  			operation:'getAuthorizationCode',
        endpoint:endpoint,
        query:{
          sellingPartnerId:'XXXXXXXXXXXXXX',
          developerId:'XXXXXXXXXXXX',
          mwsAuthToken:'<KEY>'
        }
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('InvalidInput');
      expect(e.message).to.equal('Invalid partyId: XXXXXXXXXXXXXX');
    }
	});

});

<|start_filename|>lib/resources/versions/vendor_shipments/vendorShipments_v1.js<|end_filename|>
module.exports = {
  'v1':{
    SubmitShipmentConfirmations:(req_params) => {
      return Object.assign(req_params, {
        method:'POST',
        api_path:'/vendor/shipping/v1/shipmentConfirmations',
        restore_rate:0.1
      });
    }
  }
};

<|start_filename|>lib/operations.js<|end_filename|>
// As the original design of the module (<= v0.3.7) didn't keep in mind the possibility of having more versions of the same endpoints
// and as a result different versions of the same operation, we had to replace original operation-only based calls to the API 
// with a new concept that includes endpoints and version-specific operation calls

// In order to prevent breaking changes we need this operation to endpoint mapping
module.exports = (endpoints) => {
	// Map the correct endpoint to the operation and return as key/value
	return {
		...Object.keys(endpoints).reduce((eps, ep) => {
			return Object.assign(eps, {
				...require('./resources/' + ep)[ep].__operations.reduce((ops, op) => {ops[op] = ep; return ops;}, {})
			});
		}, {})
	};
};

<|start_filename|>test/config.js<|end_filename|>
require('dotenv').config({path:__dirname + '/.env.test'});

module.exports = {
	region:process.env.REGION,
	refresh_token:process.env.REFRESH_TOKEN,
	app_client:{
		id:process.env.SELLING_PARTNER_APP_CLIENT_ID,
		secret:process.env.SELLING_PARTNER_APP_CLIENT_SECRET
	},
	aws_user:{
		id:process.env.AWS_ACCESS_KEY_ID,
    secret:process.env.AWS_SECRET_ACCESS_KEY,
    role:process.env.AWS_SELLING_PARTNER_ROLE
	},
  marketplace_id:process.env.MARKETPLACE_ID,
  asin:process.env.ASIN,
  sku:process.env.SKU,
  country_code:process.env.COUNTRY_CODE,
  currency_code:process.env.CURRENCY_CODE,
  order_id:process.env.ORDER_ID,
  seller_id:process.env.SELLER_ID
};

<|start_filename|>lib/resources/vendorShipments.js<|end_filename|>
module.exports = {
  vendorShipments:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'SubmitShipmentConfirmations'
  	],
    ...require('./versions/vendor_shipments/vendorShipments_v1')
  }
};

<|start_filename|>lib/resources/sellers.js<|end_filename|>
module.exports = {
  sellers:{
    __versions:[
    	'v1'
    ],
    __operations:[
    'getMarketplaceParticipations'
    ],
    ...require('./versions/sellers/sellers_v1.js')
  }
};

<|start_filename|>lib/resources/productTypeDefinitions.js<|end_filename|>
module.exports = {
  productTypeDefinitions:{
    __versions:[
      '2020-09-01'
    ],
    __operations:[
      'searchDefinitionsProductTypes',
      'getDefinitionsProductType'
    ],
    ...require('./versions/product_type_definitions/productTypeDefinitions_2020-09-01')
  }
};

<|start_filename|>lib/resources/versions/tokens/tokens_2021-03-01.js<|end_filename|>
module.exports = {
  '2021-03-01':{
    createRestrictedDataToken:(req_params) => {
      return Object.assign(req_params, {
        method:'POST',
        api_path:'/tokens/2021-03-01/restrictedDataToken',
        restore_rate:1
      });
    }
  }
};

<|start_filename|>lib/resources/services.js<|end_filename|>
module.exports = {
   services:{
    __versions:[
      'v1'
    ],
    __operations:[
      'getServiceJobByServiceJobId',
      'cancelServiceJobByServiceJobId',
      'completeServiceJobByServiceJobId',
      'getServiceJobs',
      'addAppointmentForServiceJobByServiceJobId',
      'rescheduleAppointmentForServiceJobByServiceJobId',
      'assignAppointmentResources',
      'setAppointmentFulfillmentData',
      'updateSchedule',
      'createReservation',
      'updateReservation',
      'cancelReservation',
      'createServiceDocumentUploadDestination'
    ],
    ...require('./versions/services/services_v1')
  }
};

<|start_filename|>lib/resources/versions/sellers/sellers_v1.js<|end_filename|>
module.exports = {
	'v1':{
	  getMarketplaceParticipations:(req_params) => {
	    return Object.assign(req_params, {
	      method:'GET',
	      api_path:'/sellers/v1/marketplaceParticipations',
	      restore_rate:60
	    });
	  }
	}
};

<|start_filename|>lib/resources/feeds.js<|end_filename|>
module.exports = {
   feeds:{
    __versions:[
      '2020-09-04',
      '2021-06-30'
    ],
    __operations:[
      'getFeeds',
      'createFeed',
      'getFeed',
      'cancelFeed',
      'createFeedDocument',
      'getFeedDocument'
    ],
    ...require('./versions/feeds/feeds_2020-09-04'),
    ...require('./versions/feeds/feeds_2021-06-30')
  }
};

<|start_filename|>lib/resources/versions/fba_inventory/fbaInventory_v1.js<|end_filename|>
module.exports = {
	'v1':{
	  // getInventorySummaries seems to be available in "NA" region only --> comparable to ListInventorySupply in MWS API
	  // May return a pagination value that includes a nextToken --> this pagination will be added to the returned payload
	  getInventorySummaries:(req_params) => {
	    return Object.assign(req_params, {
	      method:'GET',
	      api_path:'/fba/inventory/v1/summaries',
	      restore_rate:0.5
	    });
	  }
	}
};

<|start_filename|>lib/resources/fbaInventory.js<|end_filename|>
module.exports = {
 	fbaInventory:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'getInventorySummaries'
  	],
    ...require('./versions/fba_inventory/fbaInventory_v1')
  }
};

<|start_filename|>lib/resources/versions/vendor_invoices/vendorInvoices_v1.js<|end_filename|>
module.exports = {
  'v1':{
    submitInvoices:(req_params) => {
      return Object.assign(req_params, {
        method:'POST',
        api_path:'/vendor/payments/v1/invoices',
        restore_rate:0.1
      });
    }
  }
};

<|start_filename|>lib/resources/vendorTransactionStatus.js<|end_filename|>
module.exports = {
  vendorTransactionStatus:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'getTransaction'
  	],
    ...require('./versions/vendor_transaction_status/vendorTransactionStatus_v1')
  }
};

<|start_filename|>test/specs/operations/orders.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;
const moment = require('moment');

const endpoint = 'orders';

describe(endpoint, async function(){

	it('should return orders created in date range for marketplace', async function(){
		let res = await this.sellingPartner.callAPI({
			operation:'getOrders',
      endpoint:endpoint,
			query:{
        MarketplaceIds:this.config.marketplace_id,
        CreatedBefore:moment().startOf('day').toISOString(),
      	CreatedAfter:moment().startOf('day').subtract(1, 'month').toISOString()
      }
    });
    expect(res).to.be.a('object');
    expect(moment(res.CreatedBefore).isValid()).to.equal(true);
    expect(res.Orders).to.be.a('array');
	});

  it('should return order for order id', async function(){
    if (this.config.order_id){
      let res = await this.sellingPartner.callAPI({
        operation:'getOrder',
        endpoint:endpoint,
        path:{
          orderId:this.config.order_id
        }
      });
      expect(res).to.be.a('object');
      expect(res.AmazonOrderId).to.equal(this.config.order_id);
      expect(moment(res.PurchaseDate).isValid()).to.equal(true);
      expect(moment(res.LastUpdateDate).isValid()).to.equal(true);
      expect(['Pending', 'Unshipped', 'PartiallyShipped', 'Shipped', 'Canceled', 'Unfulfillable', 'InvoiceUnconfirmed', 'PendingAvailability']).to.include(res.OrderStatus);
    } else {
      this.skip();
    }
  });

  it('should return buyer information for order id', async function(){
    if (this.config.order_id){
      let res = await this.sellingPartner.callAPI({
        operation:'getOrderBuyerInfo',
        endpoint:endpoint,
        path:{
          orderId:this.config.order_id
        }
      });
      expect(res).to.be.a('object');
      expect(res.AmazonOrderId).to.equal(this.config.order_id);
    } else {
      this.skip();
    }
  });

  it('should return buyer information for order id', async function(){
    if (this.config.order_id){
      let res = await this.sellingPartner.callAPI({
        operation:'getOrderAddress',
        endpoint:endpoint,
        path:{
          orderId:this.config.order_id
        }
      });
      expect(res).to.be.a('object');
      expect(res.AmazonOrderId).to.equal(this.config.order_id);
    } else {
      this.skip();
    }
  });

  it('should return order item information for order id', async function(){
    if (this.config.order_id){
      let res = await this.sellingPartner.callAPI({
        operation:'getOrderItems',
        endpoint:endpoint,
        path:{
          orderId:this.config.order_id
        }
      });
      expect(res).to.be.a('object');
      expect(res.AmazonOrderId).to.equal(this.config.order_id);
      expect(res.OrderItems).to.be.a('array');
    } else {
      this.skip();
    }
  });

  it('should return buyer inforamtion in order items for order id', async function(){
    if (this.config.order_id){
      let res = await this.sellingPartner.callAPI({
        operation:'getOrderItemsBuyerInfo',
        endpoint:endpoint,
        path:{
          orderId:this.config.order_id
        }
      });
      expect(res).to.be.a('object');
      expect(res.AmazonOrderId).to.equal(this.config.order_id);
      expect(res.OrderItems).to.be.a('array');
    } else {
      this.skip();
    }
  });

});

<|start_filename|>test/specs/operations/solicitations.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'solicitations';

describe(endpoint, async function(){

  it('should return list of solicitation types available for order', async function(){
    if (this.config.order_id){
      let res = await this.sellingPartner.callAPI({
        operation:'getSolicitationActionsForOrder',
        endpoint:endpoint,
        path:{
          amazonOrderId:this.config.order_id
        },
        query:{
          marketplaceIds:this.config.marketplace_id
        }
      });
      expect(res).to.be.a('object');
      if (res.errors){
        expect(res.errors).to.be.a('array');
      } else {
        expect(res._links).to.be.a('object');
        expect(res._links.actions).to.be.a('array');
        expect(res._links.self).to.be.a('object');
        expect(res._links.self.href).to.be.a('string');
      }
    } else {
      this.skip();
    }
  }); 

});

<|start_filename|>test/specs/operations/fbaSmallAndLight.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'fbaSmallAndLight';

describe(endpoint, async function(){

	it('should return the small and light enrollment status for sku', async function(){
    if (this.config.sku){
  		let res = await this.sellingPartner.callAPI({
  			operation:'getSmallAndLightEnrollmentBySellerSKU',
        endpoint:endpoint,
        path:{
          sellerSKU:this.config.sku
        },
        query:{
          marketplaceIds:this.config.marketplace_id
        }
      });
      expect(res).to.be.a('object');
      expect(res.sellerSKU).to.equal(this.config.sku);
      expect(res.marketplaceId).to.equal(this.config.marketplace_id);
      expect(res.status).to.be.a('string');
      expect(['ENROLLED', 'NOT_ENROLLED']).to.include(res.status);
    } else {
      this.skip();
    }
	});

  it('should return the small and light eligibility for sku', async function(){
    if (this.config.sku){
      try {
        let res = await this.sellingPartner.callAPI({
          operation:'getSmallAndLightEligibilityBySellerSKU',
          endpoint:endpoint,
          path:{
            sellerSKU:this.config.sku
          },
          query:{
            marketplaceIds:this.config.marketplace_id
          }
        });
        expect(res).to.be.a('object');
        expect(res.sellerSKU).to.equal(this.config.sku);
        expect(res.marketplaceId).to.equal(this.config.marketplace_id);
        expect(res.status).to.be.a('string');
        expect(['ELIGIBLE', 'NOT_ELIGIBlE']).to.include(res.status);
      } catch(e){
        // TODO: Check what could be other error reasons?
        expect(['EXPECTED_SALES_VELOCITY_TOO_LOW', 'UNCOMMINGLING_FNSKU_ON_SHIPMENT', 'INVALID_ASIN_PRICE']).to.include(e.code);
      }
    } else {
      this.skip();
    }
  });

  it('should return the small and light fee estimates for asin', async function(){
    let res = await this.sellingPartner.callAPI({
      operation:'getSmallAndLightFeePreview',
      endpoint:endpoint,
      body:{
        marketplaceId:this.config.marketplace_id,
        items:[{
          asin:this.config.asin,
          price:{
            currencyCode:this.config.currency_code,
            amount:9.99
          }
        }]
      }
    });
    expect(res).to.be.a('object').to.have.property('data').to.be.a('array');
    expect(res.data[0].totalFees).to.be.a('object');
    expect(res.data[0].price).to.be.a('object');
    expect(res.data[0].asin).to.equal(this.config.asin);
    expect(res.data[0].feeBreakdown).to.be.a('array');
  });

});

<|start_filename|>lib/resources/finances.js<|end_filename|>
module.exports = {
   finances:{
    __versions:[
      'v0'
    ],
    __operations:[
      'listFinancialEventGroups',
      'listFinancialEventsByGroupId',
      'listFinancialEventsByOrderId',
      'listFinancialEvents'
    ],
    ...require('./versions/finances/finances_v0')
  }
};

<|start_filename|>lib/resources/productPricing.js<|end_filename|>
module.exports = {
   productPricing:{
    __versions:[
      'v0'
    ],
    __operations:[
      'getPricing',
      'getCompetitivePricing',
      'getListingOffers',
      'getItemOffers'
    ],
    ...require('./versions/product_pricing/productPricing_v0')
  }
};

<|start_filename|>test/specs/operations/productPricing.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'productPricing';

describe(endpoint, async function(){

  it('should return pricing information for asin', async function(){
    let res = await this.sellingPartner.callAPI({
      operation:'getPricing',
      endpoint:endpoint,
      query:{
        MarketplaceId:this.config.marketplace_id,
        Asins:this.config.asin,
        ItemType:'Asin'
      }
    });
    expect(res).to.be.a('array');
    expect(res[0].status).to.be.a('string');
    expect(res[0].ASIN).to.equal(this.config.asin);
  });

	it('should return pricing information for sku', async function(){
    if (this.config.sku){
  		let res = await this.sellingPartner.callAPI({
  			operation:'getPricing',
        endpoint:endpoint,
  			query:{
          MarketplaceId:this.config.marketplace_id,
          Skus:this.config.sku,
          ItemType:'Sku'
        }
      });
      expect(res).to.be.a('array');
      expect(res[0].status).to.be.a('string');
      expect(res[0].SellerSKU).to.equal(this.config.sku);
    } else {
      this.skip();
    }
	});

  it('should return competitive pricing information for asin', async function(){
    let res = await this.sellingPartner.callAPI({
      operation:'getCompetitivePricing',
      endpoint:endpoint,
      query:{
        MarketplaceId:this.config.marketplace_id,
        Asins:this.config.asin,
        ItemType:'Asin'
      }
    });
    expect(res).to.be.a('array');
    expect(res[0].status).to.be.a('string');
    expect(res[0].ASIN).to.equal(this.config.asin);
  });

  it('should return competitive pricing information for sku', async function(){
    if (this.config.sku){
      let res = await this.sellingPartner.callAPI({
        operation:'getCompetitivePricing',
        endpoint:endpoint,
        query:{
          MarketplaceId:this.config.marketplace_id,
          Skus:this.config.sku,
          ItemType:'Sku'
        }
      });
      expect(res).to.be.a('array');
      expect(res[0].status).to.be.a('string');
      expect(res[0].SellerSKU).to.equal(this.config.sku);
    } else {
      this.skip();
    }
  });

  // Docs wrong! --> https://github.com/amzn/selling-partner-api-docs/blob/main/references/product-pricing-api/productPricingV0.md#getoffersresult
  // Marketplace is NOT capitalized in response
  // It should always return an offers array, but if we put in an item condition without offers it is not included in response
  it('should return lowest priced offers for sku', async function(){
    if (this.config.sku){
      let res = await this.sellingPartner.callAPI({
        operation:'getListingOffers',
        endpoint:endpoint,
        path:{
          SellerSKU:this.config.sku
        },
        query:{
          MarketplaceId:this.config.marketplace_id,
          ItemCondition:'New'
        }
      });
      expect(res).to.be.a('object');
      expect(res.marketplaceId).to.equal(this.config.marketplace_id);
      expect(res.SKU).to.equal(this.config.sku);
      expect(res.ItemCondition).to.equal('New');
      expect(res.status).to.be.a('string');
      expect(res.Identifier).to.be.a('object');
      expect(res.Summary).to.be.a('object');
      // expect(res.Offers).to.be.a('array');
    } else {
      this.skip();
    }
  });

  // Docs wrong! --> https://github.com/amzn/selling-partner-api-docs/blob/main/references/product-pricing-api/productPricingV0.md#getoffersresult
  // Marketplace is NOT capitalized in response
  // It should always return an offers array, but if we put in an item condition without offers it is not included in response
  it('should return lowest priced offers for asin', async function(){
    let res = await this.sellingPartner.callAPI({
      operation:'getItemOffers',
      endpoint:endpoint,
      path:{
        Asin:this.config.asin
      },
      query:{
        MarketplaceId:this.config.marketplace_id,
        ItemCondition:'New'
      }
    });
    expect(res).to.be.a('object');
    expect(res.marketplaceId).to.equal(this.config.marketplace_id);
    expect(res.ASIN).to.equal(this.config.asin);
    expect(res.ItemCondition).to.equal('New');
    expect(res.status).to.be.a('string');
    expect(res.Identifier).to.be.a('object');
    expect(res.Summary).to.be.a('object');
    // expect(res.Offers).to.be.a('array');
  });

});

<|start_filename|>lib/resources/productFees.js<|end_filename|>
module.exports = {
   productFees:{
    __versions:[
      'v0'
    ],
    __operations:[
      'getMyFeesEstimateForSKU',
      'getMyFeesEstimateForASIN'
    ],
    ...require('./versions/product_fees/productFees_v0')
  }
};

<|start_filename|>lib/resources/versions/fba_inbound_eligibility/fbaInboundEligibility_v1.js<|end_filename|>
module.exports = {
	'v1':{
	  getItemEligibilityPreview:(req_params) => {
	    return Object.assign(req_params, {
	      method:'GET',
	      api_path:'/fba/inbound/v1/eligibility/itemPreview',
	      restore_rate:1
	    });
	  }
	}
};

<|start_filename|>lib/resources/aplusContent.js<|end_filename|>
module.exports = {
  aplusContent:{
    __versions:[
      '2020-11-01'
     ],
    __operations:[
      'searchContentDocuments',
      'createContentDocument',
      'getContentDocument',
      'updateContentDocument',
      'listContentDocumentAsinRelations',
      'postContentDocumentAsinRelations',
      'validateContentDocumentAsinRelations',
      'searchContentPublishRecords',
      'postContentDocumentApprovalSubmission',
      'postContentDocumentSuspendSubmission'
    ],
    ...require('./versions/aplus_content/aplusContent_2020-11-01')
  }
};

<|start_filename|>lib/resources/fbaInboundEligibility.js<|end_filename|>
module.exports = {
 	fbaInboundEligibility:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'getItemEligibilityPreview'
  	],
    ...require('./versions/fba_inbound_eligibility/fbaInboundEligibility_v1')
  }
};

<|start_filename|>test/specs/configErrors.spec.js<|end_filename|>
const SellingPartnerAPI = require('../../index');
const chai = require('chai');
const expect = chai.expect;

describe('configErrors', async function(){

	it('should return a missing refresh token error', async function(){
    try {
      new SellingPartnerAPI({
        region:this.config.region,
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('NO_REFRESH_TOKEN_PROVIDED');
    }
	});

  it('should return an invalid region error', async function(){
    try {
      new SellingPartnerAPI({
        region:'de',
        refresh_token:this.config.refresh_token
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('NO_VALID_REGION_PROVIDED');
    }
  });

  it('should return a no operation given error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        access_token:this.config.access_token,
        role_credentials:this.config.role_credentials
      });
      await sellingPartner.callAPI({});
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('NO_OPERATION_GIVEN');
    }
  });

  it('should return an invalid operation error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        access_token:this.config.access_token,
        role_credentials:this.config.role_credentials,
        options:{
          only_grantless_operations:true
        }
      });
      await sellingPartner.callAPI({
        operation:'sellers.getMarketplaceParticipations'
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('INVALID_OPERATION_ERROR');
    }
  });

  it('should return an endpoint not found error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        access_token:this.config.access_token,
        role_credentials:this.config.role_credentials
      });
      await sellingPartner.callAPI({
        operation:'getMarketplaceParticipations',
        endpoint:'invalidEndpoint'
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('ENDPOINT_NOT_FOUND');
    }
  });

  it('should return an invalid operation for endpoint error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        access_token:this.config.access_token,
        role_credentials:this.config.role_credentials
      });
      await sellingPartner.callAPI({
        operation:'getMarketplaceParticipations',
        endpoint:'catalogItems'
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('INVALID_OPERATION_FOR_ENDPOINT');
    }
  });

  it('should return a no scope error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        options:{
          only_grantless_operations:true
        }
      });
      await sellingPartner.refreshAccessToken();
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('NO_SCOPE_PROVIDED');
    }
  });

  it('should return a no access token or role credentials error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        options:{
          auto_request_tokens:false
        }
      });
      await sellingPartner.callAPI({
        operation:'sellers.getMarketplaceParticipations'
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('NO_ACCESS_TOKEN_AND_OR_ROLE_CREDENTIALS_PRESENT');
    }
  });

  it('should return a no operation given error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token
      });
      await sellingPartner.callAPI({});
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('NO_OPERATION_GIVEN');
    }
  });

  it('should return an unknown operation error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token
      });
      await sellingPartner.callAPI({
        operation:'unknownOperation',
        endpoint:'sellers'
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('OPERATION_NOT_FOUND');
    }
  });

  it('should return an invalid sandbox parameters error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        options:{
          use_sandbox:true
        }
      });
      await sellingPartner.callAPI({
        operation:'listCatalogItems',
        endpoint:'catalogItems',
        query:{
          MarketplaceId:'TEST',
          SellerSKU:'TEST'
        }
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('INVALID_SANDBOX_PARAMETERS');
    }
  });

  it('should return an invalid refresh token error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:'<PASSWORD>'
      });
      await sellingPartner.callAPI({
        operation:'sellers.getMarketplaceParticipations'
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('invalid_grant');
    }
  });

  it('should return an invalid method error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token
      });
      await sellingPartner.callAPI({
        api_path:'/sellers/v1/marketplaceParticipations'
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('NO_VALID_METHOD_PROVIDED');
    }
  });

  it('should return an invalid endpoints error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        endpoints_versions:{
          'invalidEndpoint':'v0'
        }
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('VERSION_DEFINED_FOR_INVALID_ENDPOINTS');
    }
  });

  it('should return an invalid version for endpoints error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        endpoints_versions:{
          'sellers':'unknownVersion'
        }
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('INVALID_VERSION_FOR_ENDPOINTS');
    }
  });

  it('should return an invalid version error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token
      });
      await sellingPartner.callAPI({
        operation:'getMarketplaceParticipations',
        endpoint:'sellers',
        options:{
          version:'unknownVersion'
        }
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('INVALID_VERSION');
    }
  });

  it('should return an operation not found for version error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        refresh_token:this.config.refresh_token,
        options:{
          version_fallback:false
        }
      });
      await sellingPartner.callAPI({
        operation:'listCatalogCategories',
        endpoint:'catalogItems',
        query:{
          MarketplaceId:this.config.marketplace_id,
          ASIN:this.config.asin
        },
        options:{
          version:'2020-12-01'
        }
      });
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('OPERATION_NOT_FOUND_FOR_VERSION');
    }
  });

  it('should return an invalid code parameter error', async function(){
    try {
      let sellingPartner = new SellingPartnerAPI({
        region:this.config.region,
        options:{
          only_grantless_operations:true
        }
      });
      await sellingPartner.exchange('invalid_auth_code');
    } catch(e){
      expect(e).to.be.an('error');
      expect(e.code).to.equal('invalid_request');
      expect(e.message).to.equal('The request has an invalid parameter : code');
    }
  });

});

<|start_filename|>test/specs/operations/vendorDirectFulfillmentOrders.spec.js<|end_filename|>
const chai = require('chai');
const expect = chai.expect;

const endpoint = 'vendorDirectFulfillmentOrders';

describe(endpoint, async function(){

  // TODO: Define tests

});

<|start_filename|>lib/resources/uploads.js<|end_filename|>
module.exports = {
   uploads:{
    __versions:[
      '2020-11-01'
    ],
    __operations:[
      'createUploadDestinationForResource'
    ],
    ...require('./versions/uploads/uploads_2020-11-01')
  }
};

<|start_filename|>lib/resources/vendorDirectFulfillmentPayments.js<|end_filename|>
module.exports = {
  vendorDirectFulfillmentPayments:{
    __versions:[
    	'v1'
  	],
    __operations:[
    	'submitInvoice'
  	],
    ...require('./versions/vendor_direct_fulfillment_payments/vendorDirectFulfillmentPayments_v1')
  }
};