ebay-mcp 1.17.0 → 1.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -53
- package/build/api/account-management/accountV2.js +347 -0
- package/build/api/account-management/finances.js +289 -0
- package/build/api/analytics-and-report/analytics.js +1 -1
- package/build/api/{client.js → client/ebayApiClient.js} +83 -36
- package/build/api/{clientTrading.js → client/tradingApiClient.js} +22 -22
- package/build/api/communication/feedback.js +21 -21
- package/build/api/communication/message.js +23 -23
- package/build/api/communication/negotiation.js +7 -7
- package/build/api/communication/notification.js +52 -52
- package/build/api/developer/developer.js +3 -3
- package/build/api/{index.js → ebaySellerApi.js} +28 -4
- package/build/api/listing-management/feed.js +91 -0
- package/build/api/listing-management/feedReportTasks.js +107 -0
- package/build/api/listing-management/feedRequest.js +91 -0
- package/build/api/listing-management/feedSchedules.js +95 -0
- package/build/api/listing-management/feedTasks.js +91 -0
- package/build/api/listing-management/items.js +22 -22
- package/build/api/listing-management/locations.js +4 -4
- package/build/api/listing-management/media.js +14 -14
- package/build/api/listing-management/mediaAttach.js +14 -14
- package/build/api/listing-management/mediaResources.js +22 -39
- package/build/api/listing-management/offerFormat.js +1 -1
- package/build/api/listing-management/offers.js +23 -23
- package/build/api/listing-management/stores.js +203 -0
- package/build/api/listing-metadata/charity.js +76 -0
- package/build/api/listing-metadata/metadata.js +31 -11
- package/build/api/listing-metadata/metadataShipping.js +117 -0
- package/build/api/listing-metadata/taxonomy.js +52 -7
- package/build/api/marketing-and-promotions/ads.js +34 -34
- package/build/api/marketing-and-promotions/campaigns.js +21 -21
- package/build/api/marketing-and-promotions/promotions.js +24 -24
- package/build/api/marketing-and-promotions/reports.js +7 -7
- package/build/api/marketing-and-promotions/shared.js +0 -3
- package/build/api/order-management/dispute.js +11 -9
- package/build/api/order-management/fulfillment.js +4 -4
- package/build/api/order-management/logistics.js +150 -0
- package/build/api/other/browseMappers.js +15 -13
- package/build/api/other/browseValidators.js +4 -4
- package/build/api/other/edelivery.js +19 -19
- package/build/api/other/finding.js +13 -13
- package/build/api/other/translation.js +2 -2
- package/build/api/other/vero.js +2 -2
- package/build/api/shared/download.js +79 -0
- package/build/api/shared/location.js +33 -0
- package/build/api/shared/request.js +69 -11
- package/build/api/trading/listingFormat.js +37 -30
- package/build/api/trading/trading.js +33 -33
- package/build/auth/credentialSession.js +5 -5
- package/build/auth/oauth.js +3 -3
- package/build/auth/oauthHelper.js +24 -24
- package/build/auth/oauthMetadata.js +4 -4
- package/build/auth/oauthMiddleware.js +16 -16
- package/build/auth/scopeUtils.js +24 -3
- package/build/auth/tokenVerifier.js +23 -23
- package/build/config/environment.js +10 -45
- package/build/config/oauthScopes.js +132 -0
- package/build/config/toolFamilies.js +4 -0
- package/build/index.js +5 -5
- package/build/mcp/httpTransport.js +32 -20
- package/build/mcp/runtime.js +12 -12
- package/build/mcp/toolGating.js +6 -12
- package/build/mcp/uiBridge.js +3 -3
- package/build/schemas/account-management/account.js +2 -52
- package/build/schemas/account-management/accountV2.js +294 -0
- package/build/schemas/account-management/finances.js +143 -0
- package/build/schemas/analytics/analytics.js +2 -48
- package/build/schemas/communication/messages.js +2 -46
- package/build/schemas/developer/developer.js +1 -27
- package/build/schemas/fulfillment/logistics.js +115 -0
- package/build/schemas/fulfillment/orders.js +2 -47
- package/build/schemas/inventory-management/feed.js +217 -0
- package/build/schemas/inventory-management/inventory.js +2 -50
- package/build/schemas/inventory-management/media.js +2 -2
- package/build/schemas/inventory-management/mediaDocuments.js +1 -1
- package/build/schemas/inventory-management/stores.js +42 -0
- package/build/schemas/marketing/ads.js +2 -2
- package/build/schemas/marketing/campaigns.js +2 -2
- package/build/schemas/marketing/common.js +2 -2
- package/build/schemas/marketing/marketing.js +0 -1
- package/build/schemas/marketing/promotions.js +2 -2
- package/build/schemas/marketing/reports.js +2 -2
- package/build/schemas/metadata/metadata.js +2 -90
- package/build/schemas/metadata/shipping.js +16 -0
- package/build/schemas/other/browse.js +2 -2
- package/build/schemas/other/finding.js +2 -2
- package/build/schemas/other/otherApis.js +2 -114
- package/build/schemas/taxonomy/categoryTree.js +8 -0
- package/build/schemas/taxonomy/charity.js +41 -0
- package/build/schemas/taxonomy/taxonomy.js +2 -69
- package/build/scripts/autoSetup.js +3 -3
- package/build/scripts/{buildUi.js → buildMcpApps.js} +13 -13
- package/build/scripts/devSync.js +34 -70
- package/build/scripts/diagnostics.js +15 -16
- package/build/scripts/interactiveSetup.js +25 -25
- package/build/scripts/scopeHelper.js +2 -2
- package/build/scripts/securityChecker.js +20 -20
- package/build/scripts/setup.js +76 -76
- package/build/scripts/setupShared.js +5 -30
- package/build/scripts/setupValidator.js +9 -9
- package/build/scripts/setupWizard.js +23 -23
- package/build/scripts/skills.js +14 -17
- package/build/scripts/specRefs.js +55 -0
- package/build/scripts/specSources.js +144 -0
- package/build/serverHttp.js +4 -7
- package/build/skills/content.js +13 -13
- package/build/skills/install.js +9 -9
- package/build/skills/registrySnapshot.js +7 -3
- package/build/skills/render.js +2 -2
- package/build/tools/categories/accountV2.js +106 -0
- package/build/tools/categories/charity.js +21 -0
- package/build/tools/categories/communication.js +1 -1
- package/build/tools/categories/connector.js +19 -16
- package/build/tools/categories/feed.js +183 -0
- package/build/tools/categories/finances.js +86 -0
- package/build/tools/categories/fulfillment.js +4 -4
- package/build/tools/categories/index.js +12 -2
- package/build/tools/categories/inventory.js +1 -1
- package/build/tools/categories/logistics.js +52 -0
- package/build/tools/categories/metadata.js +38 -1
- package/build/tools/categories/stores.js +64 -0
- package/build/tools/categories/taxonomy.js +18 -1
- package/build/tools/categories/tokenManagement.js +8 -8
- package/build/tools/contracts.js +2 -2
- package/build/tools/defineTool.js +27 -12
- package/build/tools/fileResult.js +30 -0
- package/build/tools/registry.js +2 -2
- package/build/tools/schemas.js +2 -2
- package/build/tools/ui/mapHelpers.js +12 -12
- package/build/tools/ui/maps.js +101 -97
- package/build/tools/ui/viewModels.js +2 -2
- package/build/utils/apiStatusFeed.js +12 -12
- package/build/utils/communication/feedback.js +3 -3
- package/build/utils/communication/message.js +3 -3
- package/build/utils/communication/negotiation.js +3 -3
- package/build/utils/communication/notification.js +3 -3
- package/build/utils/entryModule.js +33 -0
- package/build/utils/envFile.js +47 -0
- package/build/utils/envParser.js +5 -5
- package/build/utils/errors.js +4 -4
- package/build/utils/http.js +60 -41
- package/build/utils/localFeedFiles.js +61 -0
- package/build/utils/localMedia.js +18 -6
- package/build/utils/logRedaction.js +21 -0
- package/build/utils/logger.js +52 -33
- package/build/utils/other/compliance.js +3 -3
- package/build/utils/other/identity.js +3 -3
- package/build/utils/other/translation.js +3 -3
- package/build/utils/other/vero.js +3 -3
- package/build/utils/schemaHelpers.js +2 -2
- package/build/utils/tokenUtils.js +2 -2
- package/build/utils/trading/trading.js +1 -1
- package/build/utils/version.js +1 -1
- package/build/utils/zodIssues.js +14 -0
- package/package.json +9 -12
- package/README.de.md +0 -422
- package/README.es.md +0 -422
- package/README.fr.md +0 -422
- package/README.ja.md +0 -422
- package/README.ko.md +0 -422
- package/README.pt-BR.md +0 -422
- package/README.ru.md +0 -422
- package/README.zh-CN.md +0 -422
- package/build/schemas/index.js +0 -76
- package/build/schemas/marketing/jsonSchemas.js +0 -118
- package/build/scripts/downloadSpecs.js +0 -117
- package/build/scripts/updateApiStatusDoc.js +0 -71
- package/build/utils/effectSchema.js +0 -203
- package/build/utils/effectSchemaAdapter.js +0 -121
- package/build/utils/effectSchemaChainMethods.js +0 -146
- package/build/utils/effectSchemaRefinements.js +0 -104
- package/build/utils/effectSchemaStringMethods.js +0 -63
- package/build/utils/effectSchemaTypes.js +0 -47
- package/docs/auth/production_scopes.json +0 -111
- package/docs/auth/sandbox_scopes.json +0 -142
- /package/build/api/{clientRequestError.js → client/ebayClientRequestError.js} +0 -0
- /package/build/api/{rateLimitTracker.js → client/rateLimitTracker.js} +0 -0
- /package/build/api/{clientTradingError.js → client/tradingApiFailure.js} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { requestDownloadEffect, } from '../../api/shared/download.js';
|
|
2
|
+
import { locatedResourceId } from '../../api/shared/location.js';
|
|
3
|
+
import { buildEndpointParams, EbayApiError, EndpointInputError, } from '../../api/shared/request.js';
|
|
4
|
+
import { Effect } from 'effect';
|
|
5
|
+
/** Feed API base path on the default `api` host. */
|
|
6
|
+
export const FEED_BASE_PATH = '/sell/feed/v1';
|
|
7
|
+
/**
|
|
8
|
+
* Feed API path of one resource, with its ID encoded as a single path segment.
|
|
9
|
+
*
|
|
10
|
+
* @param collection - Collection path below the base path, e.g. `/task`.
|
|
11
|
+
* @param id - Resource ID.
|
|
12
|
+
* @param suffix - Optional sub-path, e.g. `/download_result_file`.
|
|
13
|
+
* @returns The full REST path.
|
|
14
|
+
* @example feedResourcePath('/task', 'TASK-1', '/upload_file')
|
|
15
|
+
*/
|
|
16
|
+
export const feedResourcePath = (collection, id, suffix = '') => `${FEED_BASE_PATH}${collection}/${encodeURIComponent(id)}${suffix}`;
|
|
17
|
+
/**
|
|
18
|
+
* Builds the query of a Feed API list endpoint with eBay's wire names.
|
|
19
|
+
*
|
|
20
|
+
* @param query - Decoded list filters and pagination.
|
|
21
|
+
* @returns Query params, or undefined when every value is omitted.
|
|
22
|
+
* @example feedListParams({ feedType: 'LMS_ORDER_REPORT', limit: 10 })
|
|
23
|
+
*/
|
|
24
|
+
export const feedListParams = (query) => buildEndpointParams({
|
|
25
|
+
feedType: { wireName: 'feed_type', value: query.feedType },
|
|
26
|
+
scheduleId: { wireName: 'schedule_id', value: query.scheduleId },
|
|
27
|
+
dateRange: { wireName: 'date_range', value: query.dateRange },
|
|
28
|
+
lookBackDays: { wireName: 'look_back_days', value: query.lookBackDays },
|
|
29
|
+
limit: { wireName: 'limit', value: query.limit },
|
|
30
|
+
offset: { wireName: 'offset', value: query.offset },
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Rejects the list filter pairs eBay documents as mutually exclusive (feedType with scheduleId,
|
|
34
|
+
* dateRange with lookBackDays) before any request is sent.
|
|
35
|
+
*
|
|
36
|
+
* @param query - Decoded list filters and pagination.
|
|
37
|
+
* @returns An Effect with the same query, or an EndpointInputError naming the conflicting filter.
|
|
38
|
+
* @example exclusiveFeedFilters({ feedType: 'LMS_ORDER_REPORT', lookBackDays: 10 })
|
|
39
|
+
*/
|
|
40
|
+
export const exclusiveFeedFilters = (query) => {
|
|
41
|
+
if (query.feedType !== undefined && query.scheduleId !== undefined) {
|
|
42
|
+
return Effect.fail(new EndpointInputError({
|
|
43
|
+
parameter: 'scheduleId',
|
|
44
|
+
message: 'Pass feedType or scheduleId, not both',
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
if (query.dateRange !== undefined && query.lookBackDays !== undefined) {
|
|
48
|
+
return Effect.fail(new EndpointInputError({
|
|
49
|
+
parameter: 'lookBackDays',
|
|
50
|
+
message: 'Pass dateRange or lookBackDays, not both',
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
return Effect.succeed(query);
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* POSTs a Feed API create request whose new resource ID arrives only in `Location`.
|
|
57
|
+
*
|
|
58
|
+
* @param client - Shared authenticated HTTP client.
|
|
59
|
+
* @param collection - Collection path below the base path, e.g. `/schedule`.
|
|
60
|
+
* @param createRequest - Generated create-request DTO.
|
|
61
|
+
* @param config - Optional per-request headers.
|
|
62
|
+
* @returns An Effect with the decoded ID and Location, or an EbayApiError.
|
|
63
|
+
* @example postFeedResource(client, '/schedule', { feedType: 'LMS_ORDER_REPORT', scheduleTemplateId: 'T-1' })
|
|
64
|
+
*/
|
|
65
|
+
export const postFeedResource = (client, collection, createRequest, config) => {
|
|
66
|
+
const path = `${FEED_BASE_PATH}${collection}`;
|
|
67
|
+
return Effect.tryPromise({
|
|
68
|
+
try: () => client.postForResponse(path, createRequest, config),
|
|
69
|
+
catch: (cause) => new EbayApiError({ method: 'POST', path, cause }), // allow-duplicate
|
|
70
|
+
}).pipe(Effect.flatMap((createdResponse) => locatedResourceId(createdResponse.headers.location, path)));
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Creates a Feed API task (HTTP 202, no body) and returns the ID from its `Location`.
|
|
74
|
+
*
|
|
75
|
+
* @param client - Shared authenticated HTTP client.
|
|
76
|
+
* @param collection - Task collection path, e.g. `/order_task`.
|
|
77
|
+
* @param taskRequest - Generated create-task DTO.
|
|
78
|
+
* @param headers - Optional request headers such as Accept-Language.
|
|
79
|
+
* @returns An Effect with the new task ID and Location, or an EbayApiError.
|
|
80
|
+
* @example postFeedTask(client, '/task', { feedType: 'LMS_ORDER_ACK', schemaVersion: '1235' })
|
|
81
|
+
*/
|
|
82
|
+
export const postFeedTask = (client, collection, taskRequest, headers) => postFeedResource(client, collection, taskRequest, headers ? { headers } : undefined).pipe(Effect.map(({ id, location }) => ({ taskId: id, location })));
|
|
83
|
+
/**
|
|
84
|
+
* Downloads a Feed API file (input, result or latest schedule report) as raw bytes.
|
|
85
|
+
*
|
|
86
|
+
* @param client - Shared authenticated HTTP client.
|
|
87
|
+
* @param path - Full Feed API download path.
|
|
88
|
+
* @returns An Effect with the bytes, content type and file name.
|
|
89
|
+
* @example downloadFeedFile(client, feedResourcePath('/task', 'TASK-1', '/download_result_file'))
|
|
90
|
+
*/
|
|
91
|
+
export const downloadFeedFile = (client, path) => requestDownloadEffect(client, path, { headers: { Accept: 'application/octet-stream' } });
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { decodeEndpointInputEffect, EndpointInputError, requestDeleteEffect, requestGetEffect, requestPutEffect, } from '../../api/shared/request.js';
|
|
2
|
+
import { createFeedScheduleInputSchema, feedScheduleIdInputSchema, feedScheduleTemplateIdInputSchema, feedTypePageInputSchema, updateFeedScheduleInputSchema, } from '../../schemas/inventory-management/feed.js';
|
|
3
|
+
import { Effect } from 'effect';
|
|
4
|
+
import { downloadFeedFile, FEED_BASE_PATH, feedListParams, feedResourcePath, postFeedResource, } from './feedRequest.js';
|
|
5
|
+
/**
|
|
6
|
+
* Creates the Feed API `schedule` resource endpoints: recurring report schedules, their
|
|
7
|
+
* latest result file, and the templates schedules subscribe to.
|
|
8
|
+
* @param client - Shared authenticated HTTP client.
|
|
9
|
+
* @returns Endpoint Effects composed into FeedApi.
|
|
10
|
+
*/
|
|
11
|
+
export const createFeedScheduleMethods = (client) => {
|
|
12
|
+
/**
|
|
13
|
+
* Lists the seller's schedules for a feed type.
|
|
14
|
+
* @param input - feedType (required), limit, offset.
|
|
15
|
+
* @returns Generated UserScheduleCollection.
|
|
16
|
+
* @example feed.getSchedules({ feedType: 'LMS_ORDER_REPORT' })
|
|
17
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/getSchedules
|
|
18
|
+
*/
|
|
19
|
+
const getSchedules = (input) => decodeEndpointInputEffect(feedTypePageInputSchema, input).pipe(Effect.flatMap((query) => requestGetEffect(client, `${FEED_BASE_PATH}/schedule`, feedListParams(query))));
|
|
20
|
+
/**
|
|
21
|
+
* Subscribes to a schedule template; eBay answers 201 with the new ID only in Location.
|
|
22
|
+
* @param input - CreateUserScheduleRequest body.
|
|
23
|
+
* @returns The new schedule ID and Location.
|
|
24
|
+
* @example feed.createSchedule({ schedule: { feedType: 'LMS_ORDER_REPORT', scheduleTemplateId: 'T-1' } })
|
|
25
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/createSchedule
|
|
26
|
+
*/
|
|
27
|
+
const createSchedule = (input) => decodeEndpointInputEffect(createFeedScheduleInputSchema, input).pipe(Effect.flatMap(({ schedule }) => {
|
|
28
|
+
const createScheduleRequest = schedule;
|
|
29
|
+
return postFeedResource(client, '/schedule', createScheduleRequest);
|
|
30
|
+
}), Effect.map(({ id, location }) => ({ scheduleId: id, location })));
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves one schedule's details and status.
|
|
33
|
+
* @param input - Schedule identifier.
|
|
34
|
+
* @returns Generated UserScheduleResponse.
|
|
35
|
+
* @example feed.getSchedule({ scheduleId: 'S-1' })
|
|
36
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/getSchedule
|
|
37
|
+
*/
|
|
38
|
+
const getSchedule = (input) => decodeEndpointInputEffect(feedScheduleIdInputSchema, input).pipe(Effect.flatMap(({ scheduleId }) => requestGetEffect(client, feedResourcePath('/schedule', scheduleId))));
|
|
39
|
+
/**
|
|
40
|
+
* Updates a schedule; the input is validated against its (possibly changed) template.
|
|
41
|
+
* @param input - Schedule identifier and UpdateUserScheduleRequest body with at least one field.
|
|
42
|
+
* @returns An Effect completing on eBay's empty HTTP 204 response.
|
|
43
|
+
* @example feed.updateSchedule({ scheduleId: 'S-1', schedule: { preferredTriggerHour: '11Z' } })
|
|
44
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/updateSchedule
|
|
45
|
+
*/
|
|
46
|
+
const updateSchedule = (input) => decodeEndpointInputEffect(updateFeedScheduleInputSchema, input).pipe(Effect.filterOrFail(({ schedule }) => Object.keys(schedule).length > 0, () => new EndpointInputError({
|
|
47
|
+
parameter: 'schedule',
|
|
48
|
+
message: 'Provide at least one schedule field to update',
|
|
49
|
+
})), Effect.flatMap(({ scheduleId, schedule }) => {
|
|
50
|
+
const updateScheduleRequest = schedule;
|
|
51
|
+
return requestPutEffect(client, feedResourcePath('/schedule', scheduleId), updateScheduleRequest);
|
|
52
|
+
}));
|
|
53
|
+
/**
|
|
54
|
+
* Deletes a schedule; it stops generating reports.
|
|
55
|
+
* @param input - Schedule identifier.
|
|
56
|
+
* @returns An Effect completing on eBay's empty HTTP 204 response.
|
|
57
|
+
* @example feed.deleteSchedule({ scheduleId: 'S-1' })
|
|
58
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/deleteSchedule
|
|
59
|
+
*/
|
|
60
|
+
const deleteSchedule = (input) => decodeEndpointInputEffect(feedScheduleIdInputSchema, input).pipe(Effect.flatMap(({ scheduleId }) => requestDeleteEffect(client, feedResourcePath('/schedule', scheduleId))));
|
|
61
|
+
/**
|
|
62
|
+
* Downloads the latest report a schedule generated.
|
|
63
|
+
* @param input - Schedule identifier.
|
|
64
|
+
* @returns File bytes (compressed or plain CSV, XML or JSON) with eBay's type and name.
|
|
65
|
+
* @example feed.getLatestResultFile({ scheduleId: 'S-1' })
|
|
66
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/getLatestResultFile
|
|
67
|
+
*/
|
|
68
|
+
const getLatestResultFile = (input) => decodeEndpointInputEffect(feedScheduleIdInputSchema, input).pipe(Effect.flatMap(({ scheduleId }) => downloadFeedFile(client, feedResourcePath('/schedule', scheduleId, '/download_result_file'))));
|
|
69
|
+
/**
|
|
70
|
+
* Retrieves one schedule template, including the fields a schedule must supply.
|
|
71
|
+
* @param input - Schedule template identifier.
|
|
72
|
+
* @returns Generated ScheduleTemplateResponse.
|
|
73
|
+
* @example feed.getScheduleTemplate({ scheduleTemplateId: 'T-1' })
|
|
74
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/getScheduleTemplate
|
|
75
|
+
*/
|
|
76
|
+
const getScheduleTemplate = (input) => decodeEndpointInputEffect(feedScheduleTemplateIdInputSchema, input).pipe(Effect.flatMap(({ scheduleTemplateId }) => requestGetEffect(client, feedResourcePath('/schedule_template', scheduleTemplateId))));
|
|
77
|
+
/**
|
|
78
|
+
* Lists the schedule templates for a feed type.
|
|
79
|
+
* @param input - feedType (required), limit, offset.
|
|
80
|
+
* @returns Generated ScheduleTemplateCollection.
|
|
81
|
+
* @example feed.getScheduleTemplates({ feedType: 'LMS_ORDER_REPORT' })
|
|
82
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/schedule/methods/getScheduleTemplates
|
|
83
|
+
*/
|
|
84
|
+
const getScheduleTemplates = (input) => decodeEndpointInputEffect(feedTypePageInputSchema, input).pipe(Effect.flatMap((query) => requestGetEffect(client, `${FEED_BASE_PATH}/schedule_template`, feedListParams(query))));
|
|
85
|
+
return {
|
|
86
|
+
getSchedules,
|
|
87
|
+
createSchedule,
|
|
88
|
+
getSchedule,
|
|
89
|
+
updateSchedule,
|
|
90
|
+
deleteSchedule,
|
|
91
|
+
getLatestResultFile,
|
|
92
|
+
getScheduleTemplate,
|
|
93
|
+
getScheduleTemplates,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { decodeEndpointInputEffect, requestGetEffect, requestPostEffect, requireObjectEffect, requireStringEffect, } from '../../api/shared/request.js';
|
|
2
|
+
import { createFeedTaskInputSchema, feedTaskIdInputSchema, getFeedTasksInputSchema, } from '../../schemas/inventory-management/feed.js';
|
|
3
|
+
import { Effect } from 'effect';
|
|
4
|
+
import { downloadFeedFile, exclusiveFeedFilters, FEED_BASE_PATH, feedListParams, feedResourcePath, postFeedTask, } from './feedRequest.js';
|
|
5
|
+
/** Feed files can be up to 15 MB, far larger than JSON calls. */
|
|
6
|
+
const UPLOAD_TIMEOUT_MS = 10 * 60_000;
|
|
7
|
+
/** Multipart body documented for uploadFile: fileName, name=file, type=form-data, then the file. */
|
|
8
|
+
const feedFileForm = (file) => {
|
|
9
|
+
const form = new FormData();
|
|
10
|
+
form.append('fileName', file.fileName);
|
|
11
|
+
form.append('name', 'file');
|
|
12
|
+
form.append('type', 'form-data');
|
|
13
|
+
// Copy into a fresh ArrayBuffer-backed view: Blob rejects Node's ArrayBufferLike-typed views.
|
|
14
|
+
form.append('file', new Blob([new Uint8Array(file.bytes)], { type: file.mimeType }), file.fileName);
|
|
15
|
+
return form;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Creates the Feed API `task` resource endpoints: upload and download tasks without filter
|
|
19
|
+
* criteria, their input/result files, and feed file uploads.
|
|
20
|
+
* @param client - Shared authenticated HTTP client.
|
|
21
|
+
* @returns Endpoint Effects composed into FeedApi.
|
|
22
|
+
*/
|
|
23
|
+
export const createFeedTaskMethods = (client) => {
|
|
24
|
+
/**
|
|
25
|
+
* Lists feed tasks by feed type or schedule, with creation-date filters and pagination.
|
|
26
|
+
* @param input - feedType or scheduleId (not both), dateRange or lookBackDays, limit, offset.
|
|
27
|
+
* @returns Generated TaskCollection.
|
|
28
|
+
* @example feed.getTasks({ feedType: 'LMS_ORDER_ACK', lookBackDays: 7 })
|
|
29
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/task/methods/getTasks
|
|
30
|
+
*/
|
|
31
|
+
const getTasks = (input = {}) => decodeEndpointInputEffect(getFeedTasksInputSchema, input).pipe(Effect.flatMap(exclusiveFeedFilters), Effect.flatMap((query) => requestGetEffect(client, `${FEED_BASE_PATH}/task`, feedListParams(query))));
|
|
32
|
+
/**
|
|
33
|
+
* Creates an upload or download task for a feed type; eBay answers 202 with only a Location.
|
|
34
|
+
* @param input - CreateTaskRequest body plus optional marketplace and Accept-Language headers.
|
|
35
|
+
* @returns The new task ID and Location.
|
|
36
|
+
* @example feed.createTask({ task: { feedType: 'LMS_ADD_FIXED_PRICE_ITEM', schemaVersion: '1423' } })
|
|
37
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/task/methods/createTask
|
|
38
|
+
*/
|
|
39
|
+
const createTask = (input) => Effect.gen(function* () {
|
|
40
|
+
const { task, marketplaceId, acceptLanguage } = yield* decodeEndpointInputEffect(createFeedTaskInputSchema, input);
|
|
41
|
+
const taskRequest = task;
|
|
42
|
+
const headers = {};
|
|
43
|
+
if (marketplaceId) {
|
|
44
|
+
headers['X-EBAY-C-MARKETPLACE-ID'] = marketplaceId;
|
|
45
|
+
}
|
|
46
|
+
if (acceptLanguage) {
|
|
47
|
+
headers['Accept-Language'] = acceptLanguage;
|
|
48
|
+
}
|
|
49
|
+
return yield* postFeedTask(client, '/task', taskRequest, headers);
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Retrieves a feed task's status and upload summary.
|
|
53
|
+
* @param input - Task identifier.
|
|
54
|
+
* @returns Generated Task.
|
|
55
|
+
* @example feed.getTask({ taskId: 'TASK-1' })
|
|
56
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/task/methods/getTask
|
|
57
|
+
*/
|
|
58
|
+
const getTask = (input) => decodeEndpointInputEffect(feedTaskIdInputSchema, input).pipe(Effect.flatMap(({ taskId }) => requestGetEffect(client, feedResourcePath('/task', taskId))));
|
|
59
|
+
/**
|
|
60
|
+
* Downloads the file previously uploaded to a task (not for LMS_ORDER_REPORT or
|
|
61
|
+
* LMS_ACTIVE_INVENTORY_REPORT tasks).
|
|
62
|
+
* @param input - Task identifier.
|
|
63
|
+
* @returns File bytes with eBay's content type and file name.
|
|
64
|
+
* @example feed.getInputFile({ taskId: 'TASK-1' })
|
|
65
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/task/methods/getInputFile
|
|
66
|
+
*/
|
|
67
|
+
const getInputFile = (input) => decodeEndpointInputEffect(feedTaskIdInputSchema, input).pipe(Effect.flatMap(({ taskId }) => downloadFeedFile(client, feedResourcePath('/task', taskId, '/download_input_file'))));
|
|
68
|
+
/**
|
|
69
|
+
* Downloads a task's generated result file once it is COMPLETED or COMPLETED_WITH_ERROR.
|
|
70
|
+
* @param input - Task identifier.
|
|
71
|
+
* @returns File bytes (often csv.gz or zipped XML) with eBay's content type and file name.
|
|
72
|
+
* @example feed.getResultFile({ taskId: 'TASK-1' })
|
|
73
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/task/methods/getResultFile
|
|
74
|
+
*/
|
|
75
|
+
const getResultFile = (input) => decodeEndpointInputEffect(feedTaskIdInputSchema, input).pipe(Effect.flatMap(({ taskId }) => downloadFeedFile(client, feedResourcePath('/task', taskId, '/download_result_file'))));
|
|
76
|
+
/**
|
|
77
|
+
* Uploads the feed file of an upload task as multipart/form-data; eBay then processes it
|
|
78
|
+
* asynchronously (QUEUED, IN_PROCESS, COMPLETED or COMPLETED_WITH_ERROR).
|
|
79
|
+
* @param input - Task identifier and a validated local feed file.
|
|
80
|
+
* @returns eBay's (empty) acknowledgement.
|
|
81
|
+
* @example feed.uploadFile({ taskId: 'TASK-1', file })
|
|
82
|
+
* @see https://developer.ebay.com/api-docs/sell/feed/resources/task/methods/uploadFile
|
|
83
|
+
*/
|
|
84
|
+
const uploadFile = (input) => Effect.gen(function* () {
|
|
85
|
+
const { taskId } = yield* decodeEndpointInputEffect(feedTaskIdInputSchema, input);
|
|
86
|
+
const file = yield* requireObjectEffect(input.file, 'file');
|
|
87
|
+
yield* requireStringEffect(file.fileName, 'file.fileName');
|
|
88
|
+
return yield* requestPostEffect(client, feedResourcePath('/task', taskId, '/upload_file'), feedFileForm(file), { timeoutMs: UPLOAD_TIMEOUT_MS });
|
|
89
|
+
});
|
|
90
|
+
return { getTasks, createTask, getTask, getInputFile, getResultFile, uploadFile };
|
|
91
|
+
};
|
|
@@ -11,7 +11,7 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```ts
|
|
14
|
-
* const
|
|
14
|
+
* const inventoryPage = await Effect.runPromise(inventoryApi.getInventoryItems({ limit: 25 }));
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
17
|
* @see https://developer.ebay.com/api-docs/sell/inventory/resources/inventory_item/methods/getInventoryItems
|
|
@@ -37,7 +37,7 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
37
37
|
*
|
|
38
38
|
* @example
|
|
39
39
|
* ```ts
|
|
40
|
-
* const
|
|
40
|
+
* const inventoryItem = await Effect.runPromise(inventoryApi.getInventoryItem({ sku: 'SKU-1' }));
|
|
41
41
|
* ```
|
|
42
42
|
*
|
|
43
43
|
* @see https://developer.ebay.com/api-docs/sell/inventory/resources/inventory_item/methods/getInventoryItem
|
|
@@ -70,11 +70,11 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
70
70
|
return Effect.gen(function* () {
|
|
71
71
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
72
72
|
const sku = yield* requireStringEffect(validatedInput.sku, 'sku');
|
|
73
|
-
const
|
|
73
|
+
const inventoryItem = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
74
74
|
const config = validatedInput.contentLanguage === undefined
|
|
75
75
|
? undefined
|
|
76
76
|
: { headers: { 'Content-Language': validatedInput.contentLanguage } };
|
|
77
|
-
return yield* requestPutEffect(client, `${basePath}/inventory_item/${sku}`,
|
|
77
|
+
return yield* requestPutEffect(client, `${basePath}/inventory_item/${sku}`, inventoryItem, config);
|
|
78
78
|
});
|
|
79
79
|
},
|
|
80
80
|
/**
|
|
@@ -106,7 +106,7 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
106
106
|
*
|
|
107
107
|
* @example
|
|
108
108
|
* ```ts
|
|
109
|
-
* const
|
|
109
|
+
* const inventoryItemResults = await Effect.runPromise(
|
|
110
110
|
* inventoryApi.bulkCreateOrReplaceInventoryItem({ body: { requests: [] } }),
|
|
111
111
|
* );
|
|
112
112
|
* ```
|
|
@@ -117,8 +117,8 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
117
117
|
const path = `${INVENTORY_BASE_PATH}/bulk_create_or_replace_inventory_item`;
|
|
118
118
|
return Effect.gen(function* () {
|
|
119
119
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
120
|
-
const
|
|
121
|
-
return yield* requestPostEffect(client, path,
|
|
120
|
+
const bulkInventoryItemRequest = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
121
|
+
return yield* requestPostEffect(client, path, bulkInventoryItemRequest);
|
|
122
122
|
});
|
|
123
123
|
},
|
|
124
124
|
/**
|
|
@@ -129,7 +129,7 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
129
129
|
*
|
|
130
130
|
* @example
|
|
131
131
|
* ```ts
|
|
132
|
-
* const
|
|
132
|
+
* const inventoryItems = await Effect.runPromise(
|
|
133
133
|
* inventoryApi.bulkGetInventoryItem({ body: { requests: [{ sku: 'SKU-1' }] } }),
|
|
134
134
|
* );
|
|
135
135
|
* ```
|
|
@@ -140,8 +140,8 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
140
140
|
const path = `${INVENTORY_BASE_PATH}/bulk_get_inventory_item`;
|
|
141
141
|
return Effect.gen(function* () {
|
|
142
142
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
143
|
-
const
|
|
144
|
-
return yield* requestPostEffect(client, path,
|
|
143
|
+
const bulkGetInventoryItemRequest = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
144
|
+
return yield* requestPostEffect(client, path, bulkGetInventoryItemRequest);
|
|
145
145
|
});
|
|
146
146
|
},
|
|
147
147
|
/**
|
|
@@ -152,7 +152,7 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
152
152
|
*
|
|
153
153
|
* @example
|
|
154
154
|
* ```ts
|
|
155
|
-
* const
|
|
155
|
+
* const priceQuantityResults = await Effect.runPromise(
|
|
156
156
|
* inventoryApi.bulkUpdatePriceQuantity({ body: { requests: [] } }),
|
|
157
157
|
* );
|
|
158
158
|
* ```
|
|
@@ -163,8 +163,8 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
163
163
|
const path = `${INVENTORY_BASE_PATH}/bulk_update_price_quantity`;
|
|
164
164
|
return Effect.gen(function* () {
|
|
165
165
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
166
|
-
const
|
|
167
|
-
return yield* requestPostEffect(client, path,
|
|
166
|
+
const bulkPriceQuantityRequest = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
167
|
+
return yield* requestPostEffect(client, path, bulkPriceQuantityRequest);
|
|
168
168
|
});
|
|
169
169
|
},
|
|
170
170
|
/**
|
|
@@ -210,8 +210,8 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
210
210
|
return Effect.gen(function* () {
|
|
211
211
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
212
212
|
const sku = yield* requireStringEffect(validatedInput.sku, 'sku');
|
|
213
|
-
const
|
|
214
|
-
return yield* requestPutEffect(client, `${basePath}/inventory_item/${sku}/product_compatibility`,
|
|
213
|
+
const productCompatibility = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
214
|
+
return yield* requestPutEffect(client, `${basePath}/inventory_item/${sku}/product_compatibility`, productCompatibility);
|
|
215
215
|
});
|
|
216
216
|
},
|
|
217
217
|
/**
|
|
@@ -281,8 +281,8 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
281
281
|
return Effect.gen(function* () {
|
|
282
282
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
283
283
|
const inventoryItemGroupKey = yield* requireStringEffect(validatedInput.inventoryItemGroupKey, 'inventoryItemGroupKey');
|
|
284
|
-
const
|
|
285
|
-
return yield* requestPutEffect(client, `${basePath}/inventory_item_group/${inventoryItemGroupKey}`,
|
|
284
|
+
const inventoryItemGroup = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
285
|
+
return yield* requestPutEffect(client, `${basePath}/inventory_item_group/${inventoryItemGroupKey}`, inventoryItemGroup);
|
|
286
286
|
});
|
|
287
287
|
},
|
|
288
288
|
/**
|
|
@@ -316,7 +316,7 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
316
316
|
*
|
|
317
317
|
* @example
|
|
318
318
|
* ```ts
|
|
319
|
-
* const
|
|
319
|
+
* const migratedListings = await Effect.runPromise(
|
|
320
320
|
* inventoryApi.bulkMigrateListing({ body: { requests: [] } }),
|
|
321
321
|
* );
|
|
322
322
|
* ```
|
|
@@ -327,8 +327,8 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
327
327
|
const path = `${INVENTORY_BASE_PATH}/bulk_migrate_listing`;
|
|
328
328
|
return Effect.gen(function* () {
|
|
329
329
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
330
|
-
const
|
|
331
|
-
return yield* requestPostEffect(client, path,
|
|
330
|
+
const bulkMigrateListingRequest = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
331
|
+
return yield* requestPostEffect(client, path, bulkMigrateListingRequest);
|
|
332
332
|
});
|
|
333
333
|
},
|
|
334
334
|
/**
|
|
@@ -380,8 +380,8 @@ export const createInventoryItemsMethods = (client) => ({
|
|
|
380
380
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
381
381
|
const listingId = yield* requireStringEffect(validatedInput.listingId, 'listingId');
|
|
382
382
|
const sku = yield* requireStringEffect(validatedInput.sku, 'sku');
|
|
383
|
-
const
|
|
384
|
-
return yield* requestPutEffect(client, `${basePath}/listing/${listingId}/sku/${sku}/locations`,
|
|
383
|
+
const locationMapping = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
384
|
+
return yield* requestPutEffect(client, `${basePath}/listing/${listingId}/sku/${sku}/locations`, locationMapping);
|
|
385
385
|
});
|
|
386
386
|
},
|
|
387
387
|
/**
|
|
@@ -46,8 +46,8 @@ export const createInventoryLocationsMethods = (client) => ({
|
|
|
46
46
|
return Effect.gen(function* () {
|
|
47
47
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
48
48
|
const merchantLocationKey = yield* requireStringEffect(validatedInput.merchantLocationKey, 'merchantLocationKey');
|
|
49
|
-
const
|
|
50
|
-
return yield* requestPostEffect(client, `${basePath}/location/${merchantLocationKey}`,
|
|
49
|
+
const inventoryLocation = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
50
|
+
return yield* requestPostEffect(client, `${basePath}/location/${merchantLocationKey}`, inventoryLocation);
|
|
51
51
|
});
|
|
52
52
|
},
|
|
53
53
|
/**
|
|
@@ -165,8 +165,8 @@ export const createInventoryLocationsMethods = (client) => ({
|
|
|
165
165
|
return Effect.gen(function* () {
|
|
166
166
|
const validatedInput = yield* requireObjectEffect(input, 'input');
|
|
167
167
|
const merchantLocationKey = yield* requireStringEffect(validatedInput.merchantLocationKey, 'merchantLocationKey');
|
|
168
|
-
const
|
|
169
|
-
return yield* requestPostEffect(client, `${basePath}/location/${merchantLocationKey}/update_location_details`,
|
|
168
|
+
const inventoryLocationUpdate = yield* requireObjectEffect(validatedInput.body, 'body');
|
|
169
|
+
return yield* requestPostEffect(client, `${basePath}/location/${merchantLocationKey}/update_location_details`, inventoryLocationUpdate);
|
|
170
170
|
});
|
|
171
171
|
},
|
|
172
172
|
});
|
|
@@ -33,23 +33,23 @@ const videoStatusFinal = (video) => video.status !== undefined && TERMINAL_VIDEO
|
|
|
33
33
|
* standing still while status requests kept going, so the boundary is checked
|
|
34
34
|
* before the first poll rather than inside the loop.
|
|
35
35
|
*
|
|
36
|
-
* @param
|
|
36
|
+
* @param durationMs - Caller-supplied duration in milliseconds, or undefined.
|
|
37
37
|
* @param fallback - Duration used when the caller supplied none.
|
|
38
38
|
* @param name - Parameter name used in the validation message.
|
|
39
39
|
* @param minimum - Smallest accepted value.
|
|
40
40
|
* @returns An Effect with the duration to use, or a tagged input error.
|
|
41
41
|
*/
|
|
42
|
-
const requireDurationMs = (
|
|
43
|
-
if (
|
|
42
|
+
const requireDurationMs = (durationMs, fallback, name, minimum) => {
|
|
43
|
+
if (durationMs === undefined) {
|
|
44
44
|
return Effect.succeed(fallback);
|
|
45
45
|
}
|
|
46
|
-
if (!Number.isFinite(
|
|
46
|
+
if (!Number.isFinite(durationMs) || durationMs < minimum) {
|
|
47
47
|
return Effect.fail(new EndpointInputError({
|
|
48
48
|
parameter: name,
|
|
49
49
|
message: `${name} must be a finite number of milliseconds of at least ${minimum} when provided`,
|
|
50
50
|
}));
|
|
51
51
|
}
|
|
52
|
-
return Effect.succeed(
|
|
52
|
+
return Effect.succeed(durationMs);
|
|
53
53
|
};
|
|
54
54
|
/**
|
|
55
55
|
* Media API client. Image and video uploads go to eBay's `apim` host and return
|
|
@@ -120,14 +120,14 @@ export class MediaApi {
|
|
|
120
120
|
const form = new FormData();
|
|
121
121
|
// Copy into a fresh ArrayBuffer-backed view: Blob rejects Node's ArrayBufferLike-typed views.
|
|
122
122
|
form.append('image', new Blob([new Uint8Array(upload.bytes)], { type: upload.mimeType }), fileName);
|
|
123
|
-
const
|
|
123
|
+
const imageResponse = yield* Effect.tryPromise({
|
|
124
124
|
try: () => client.postForResponse(url, form, {
|
|
125
125
|
absolute: true,
|
|
126
126
|
timeoutMs: UPLOAD_TIMEOUT_MS,
|
|
127
127
|
}),
|
|
128
128
|
catch: apiFailure('POST', path),
|
|
129
129
|
});
|
|
130
|
-
const imageUrl =
|
|
130
|
+
const imageUrl = imageResponse.data?.imageUrl;
|
|
131
131
|
if (!imageUrl) {
|
|
132
132
|
return yield* Effect.fail(new EbayApiError({
|
|
133
133
|
method: 'POST',
|
|
@@ -137,9 +137,9 @@ export class MediaApi {
|
|
|
137
137
|
}
|
|
138
138
|
return {
|
|
139
139
|
source: upload.source,
|
|
140
|
-
imageId: idFromLocation(
|
|
140
|
+
imageId: idFromLocation(imageResponse.headers.location),
|
|
141
141
|
imageUrl,
|
|
142
|
-
expirationDate:
|
|
142
|
+
expirationDate: imageResponse.data?.expirationDate,
|
|
143
143
|
};
|
|
144
144
|
});
|
|
145
145
|
};
|
|
@@ -166,7 +166,7 @@ export class MediaApi {
|
|
|
166
166
|
index,
|
|
167
167
|
uploaded: [...uploaded],
|
|
168
168
|
cause,
|
|
169
|
-
message: `upload of ${file.source} (file ${index + 1} of ${files.length}) failed: ${getErrorMessage(cause)}${uploaded.length > 0 ? `; already uploaded: ${uploaded.map((
|
|
169
|
+
message: `upload of ${file.source} (file ${index + 1} of ${files.length}) failed: ${getErrorMessage(cause)}${uploaded.length > 0 ? `; already uploaded: ${uploaded.map((uploadedImage) => uploadedImage.imageUrl).join(', ')}` : ''}`,
|
|
170
170
|
})));
|
|
171
171
|
uploaded.push(image);
|
|
172
172
|
}
|
|
@@ -218,17 +218,17 @@ export class MediaApi {
|
|
|
218
218
|
return Effect.gen(function* () {
|
|
219
219
|
const validated = yield* requireObjectEffect(input, 'input');
|
|
220
220
|
const title = yield* requireStringEffect(validated.title, 'title');
|
|
221
|
-
const
|
|
221
|
+
const videoRequest = {
|
|
222
222
|
title,
|
|
223
223
|
size: validated.size,
|
|
224
224
|
description: validated.description,
|
|
225
225
|
classification: ['ITEM'],
|
|
226
226
|
};
|
|
227
|
-
const
|
|
228
|
-
try: () => client.postForResponse(url,
|
|
227
|
+
const videoResponse = yield* Effect.tryPromise({
|
|
228
|
+
try: () => client.postForResponse(url, videoRequest, { absolute: true }),
|
|
229
229
|
catch: apiFailure('POST', path),
|
|
230
230
|
});
|
|
231
|
-
const videoId = idFromLocation(
|
|
231
|
+
const videoId = idFromLocation(videoResponse.headers.location);
|
|
232
232
|
if (!videoId) {
|
|
233
233
|
return yield* Effect.fail(new EbayApiError({
|
|
234
234
|
method: 'POST',
|
|
@@ -6,13 +6,13 @@ export class MediaAttachError extends Data.TaggedError('MediaAttachError') {
|
|
|
6
6
|
}
|
|
7
7
|
const dedupe = (values) => [...new Set(values)];
|
|
8
8
|
/** Keeps only the fields createOrReplaceInventoryItem accepts (drops sku/group metadata). */
|
|
9
|
-
const toInventoryItemBody = (
|
|
10
|
-
availability:
|
|
11
|
-
condition:
|
|
12
|
-
conditionDescription:
|
|
13
|
-
conditionDescriptors:
|
|
14
|
-
packageWeightAndSize:
|
|
15
|
-
product:
|
|
9
|
+
const toInventoryItemBody = (inventoryItem) => ({
|
|
10
|
+
availability: inventoryItem.availability,
|
|
11
|
+
condition: inventoryItem.condition,
|
|
12
|
+
conditionDescription: inventoryItem.conditionDescription,
|
|
13
|
+
conditionDescriptors: inventoryItem.conditionDescriptors,
|
|
14
|
+
packageWeightAndSize: inventoryItem.packageWeightAndSize,
|
|
15
|
+
product: inventoryItem.product,
|
|
16
16
|
});
|
|
17
17
|
/** `Content-Language` value for an inventory item locale such as `en_US`. */
|
|
18
18
|
const contentLanguageOf = (locale) => locale?.replace('_', '-');
|
|
@@ -68,9 +68,9 @@ const failedOutcome = (file, kind, cause) => ({
|
|
|
68
68
|
status: 'failed',
|
|
69
69
|
error: getErrorMessage(cause),
|
|
70
70
|
});
|
|
71
|
-
const attachFailure = (summary,
|
|
72
|
-
message: `${summary}. Results: ${JSON.stringify({ images:
|
|
73
|
-
result,
|
|
71
|
+
const attachFailure = (summary, attachResult) => new MediaAttachError({
|
|
72
|
+
message: `${summary}. Results: ${JSON.stringify({ images: attachResult.images, videos: attachResult.videos })}`,
|
|
73
|
+
result: attachResult,
|
|
74
74
|
});
|
|
75
75
|
/** Uploads every file, turning each failure into a `failed` outcome instead of aborting. */
|
|
76
76
|
const uploadAll = (media, input) => Effect.gen(function* () {
|
|
@@ -117,7 +117,7 @@ export const createMediaAttachMethods = (items, media) => ({
|
|
|
117
117
|
const { images, videos } = yield* uploadAll(media, input);
|
|
118
118
|
const failed = [...images, ...videos].filter((outcome) => outcome.status === 'failed');
|
|
119
119
|
const uploaded = uploadedMedia(images, videos);
|
|
120
|
-
const
|
|
120
|
+
const attachResult = {
|
|
121
121
|
sku: input.sku,
|
|
122
122
|
updated: false,
|
|
123
123
|
images,
|
|
@@ -125,10 +125,10 @@ export const createMediaAttachMethods = (items, media) => ({
|
|
|
125
125
|
...mergeMedia(snapshot.product ?? {}, input, uploaded),
|
|
126
126
|
};
|
|
127
127
|
if (failed.length > 0 && !input.allowPartial) {
|
|
128
|
-
return yield* Effect.fail(attachFailure(`${failed.length} of ${total} uploads failed; inventory item ${input.sku} was not updated (pass allowPartial: true to attach the successful ones)`,
|
|
128
|
+
return yield* Effect.fail(attachFailure(`${failed.length} of ${total} uploads failed; inventory item ${input.sku} was not updated (pass allowPartial: true to attach the successful ones)`, attachResult));
|
|
129
129
|
}
|
|
130
130
|
if (uploaded.imageUrls.length + uploaded.videoIds.length === 0) {
|
|
131
|
-
return yield* Effect.fail(attachFailure(`no media was uploaded successfully; inventory item ${input.sku} was not updated`,
|
|
131
|
+
return yield* Effect.fail(attachFailure(`no media was uploaded successfully; inventory item ${input.sku} was not updated`, attachResult));
|
|
132
132
|
}
|
|
133
133
|
// Re-read right before the complete-replacement PUT: the uploads can take
|
|
134
134
|
// minutes, and the earlier snapshot must not overwrite edits made meanwhile.
|
|
@@ -140,6 +140,6 @@ export const createMediaAttachMethods = (items, media) => ({
|
|
|
140
140
|
contentLanguage: contentLanguageOf(latest.locale),
|
|
141
141
|
body: { ...toInventoryItemBody(latest), product: { ...product, ...merged } },
|
|
142
142
|
});
|
|
143
|
-
return { ...
|
|
143
|
+
return { ...attachResult, ...merged, updated: true };
|
|
144
144
|
}),
|
|
145
145
|
});
|