@yoryoboy/bi-mcp 1.11.0 → 1.13.0
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 +4 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.js +398 -8
- package/dist/index.js.map +2 -2
- package/dist/mcp-use.json +2 -2
- package/dist/src/config/__tests__/meta.test.js +35 -0
- package/dist/src/config/__tests__/meta.test.js.map +7 -0
- package/dist/src/config/meta-store.js +109 -0
- package/dist/src/config/meta-store.js.map +7 -0
- package/dist/src/config/meta.js +0 -2
- package/dist/src/config/meta.js.map +2 -2
- package/dist/src/meta/__tests__/meta-utils.test.js +155 -0
- package/dist/src/meta/__tests__/meta-utils.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js +265 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js +311 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js +220 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js.map +7 -0
- package/dist/src/services/meta/__tests__/meta-ads.test.js +126 -0
- package/dist/src/services/meta/__tests__/meta-ads.test.js.map +7 -0
- package/dist/src/services/meta/__tests__/meta-api.test.js +70 -0
- package/dist/src/services/meta/__tests__/meta-api.test.js.map +7 -0
- package/dist/src/services/meta/meta-ads.js +94 -40
- package/dist/src/services/meta/meta-ads.js.map +2 -2
- package/dist/src/services/meta/meta-api.js +8 -6
- package/dist/src/services/meta/meta-api.js.map +2 -2
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js +165 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js +92 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js +41 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog.test.js +80 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js +85 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js +251 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-write.test.js +111 -0
- package/dist/src/services/vtex/__tests__/vtex-write.test.js.map +7 -0
- package/dist/src/services/vtex/vtex-catalog-write.js +194 -1
- package/dist/src/services/vtex/vtex-catalog-write.js.map +2 -2
- package/dist/src/services/vtex/vtex-catalog.js +118 -1
- package/dist/src/services/vtex/vtex-catalog.js.map +2 -2
- package/dist/src/services/vtex/vtex-pricing-write.js +5 -0
- package/dist/src/services/vtex/vtex-pricing-write.js.map +2 -2
- package/dist/src/services/vtex/vtex-write.js +67 -1
- package/dist/src/services/vtex/vtex-write.js.map +2 -2
- package/dist/src/tools/config/list-profiles.js +37 -6
- package/dist/src/tools/config/list-profiles.js.map +2 -2
- package/dist/src/tools/index.js.map +2 -2
- package/dist/src/tools/mercadolibre/__tests__/helpers.test.js +37 -0
- package/dist/src/tools/mercadolibre/__tests__/helpers.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js +30 -0
- package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/profile-resolution.js +4 -50
- package/dist/src/tools/mercadolibre/profile-resolution.js.map +2 -2
- package/dist/src/tools/meta/__tests__/pagination.test.js +133 -0
- package/dist/src/tools/meta/__tests__/pagination.test.js.map +7 -0
- package/dist/src/tools/meta/__tests__/profile-access.test.js +262 -0
- package/dist/src/tools/meta/__tests__/profile-access.test.js.map +7 -0
- package/dist/src/tools/meta/__tests__/read-tools.test.js +722 -0
- package/dist/src/tools/meta/__tests__/read-tools.test.js.map +7 -0
- package/dist/src/tools/meta/__tests__/schemas.test.js +103 -0
- package/dist/src/tools/meta/__tests__/schemas.test.js.map +7 -0
- package/dist/src/tools/meta/account-overview.js +37 -19
- package/dist/src/tools/meta/account-overview.js.map +2 -2
- package/dist/src/tools/meta/ad-account-info.js +31 -6
- package/dist/src/tools/meta/ad-account-info.js.map +2 -2
- package/dist/src/tools/meta/ads-performance.js +35 -21
- package/dist/src/tools/meta/ads-performance.js.map +2 -2
- package/dist/src/tools/meta/campaign-performance.js +35 -18
- package/dist/src/tools/meta/campaign-performance.js.map +2 -2
- package/dist/src/tools/meta/list-accessible-ad-accounts.js +39 -10
- package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +2 -2
- package/dist/src/tools/meta/list-accessible-businesses.js +37 -13
- package/dist/src/tools/meta/list-accessible-businesses.js.map +2 -2
- package/dist/src/tools/meta/placement-mix.js +37 -22
- package/dist/src/tools/meta/placement-mix.js.map +2 -2
- package/dist/src/tools/meta/profile-access.js +215 -0
- package/dist/src/tools/meta/profile-access.js.map +7 -0
- package/dist/src/tools/meta/schema-helpers.js +19 -0
- package/dist/src/tools/meta/schema-helpers.js.map +7 -0
- package/dist/src/tools/meta/time-series.js +40 -23
- package/dist/src/tools/meta/time-series.js.map +2 -2
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js +233 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js +649 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js +339 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js +235 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js +372 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js +57 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/write-helpers.test.js +97 -0
- package/dist/src/tools/vtex/__tests__/write-helpers.test.js.map +7 -0
- package/dist/src/tools/vtex/activate-sku.js +1 -48
- package/dist/src/tools/vtex/activate-sku.js.map +2 -2
- package/dist/src/tools/vtex/associate-specification.js +3 -54
- package/dist/src/tools/vtex/associate-specification.js.map +2 -2
- package/dist/src/tools/vtex/attach-catalog-image.js +3 -57
- package/dist/src/tools/vtex/attach-catalog-image.js.map +2 -2
- package/dist/src/tools/vtex/catalog-admin-batch.js +298 -0
- package/dist/src/tools/vtex/catalog-admin-batch.js.map +7 -0
- package/dist/src/tools/vtex/catalog-admin-categories.js +542 -0
- package/dist/src/tools/vtex/catalog-admin-categories.js.map +7 -0
- package/dist/src/tools/vtex/catalog-admin-product-specifications.js +275 -0
- package/dist/src/tools/vtex/catalog-admin-product-specifications.js.map +7 -0
- package/dist/src/tools/vtex/catalog-admin-products-skus.js +475 -0
- package/dist/src/tools/vtex/catalog-admin-products-skus.js.map +7 -0
- package/dist/src/tools/vtex/catalog-navigation-reads.js +430 -0
- package/dist/src/tools/vtex/catalog-navigation-reads.js.map +7 -0
- package/dist/src/tools/vtex/create-brand.js +1 -64
- package/dist/src/tools/vtex/create-brand.js.map +2 -2
- package/dist/src/tools/vtex/create-category.js +1 -76
- package/dist/src/tools/vtex/create-category.js.map +2 -2
- package/dist/src/tools/vtex/create-product-with-sku.js +3 -114
- package/dist/src/tools/vtex/create-product-with-sku.js.map +2 -2
- package/dist/src/tools/vtex/create-specification-value.js +3 -47
- package/dist/src/tools/vtex/create-specification-value.js.map +2 -2
- package/dist/src/tools/vtex/create-specification.js +1 -80
- package/dist/src/tools/vtex/create-specification.js.map +2 -2
- package/dist/src/tools/vtex/deactivate-sku.js +1 -48
- package/dist/src/tools/vtex/deactivate-sku.js.map +2 -2
- package/dist/src/tools/vtex/delete-all-product-specifications.js +9 -0
- package/dist/src/tools/vtex/delete-all-product-specifications.js.map +7 -0
- package/dist/src/tools/vtex/delete-all-sku-specifications.js +9 -0
- package/dist/src/tools/vtex/delete-all-sku-specifications.js.map +7 -0
- package/dist/src/tools/vtex/delete-brand.js +9 -0
- package/dist/src/tools/vtex/delete-brand.js.map +7 -0
- package/dist/src/tools/vtex/delete-catalog-image.js +9 -0
- package/dist/src/tools/vtex/delete-catalog-image.js.map +7 -0
- package/dist/src/tools/vtex/delete-product-specification.js +9 -0
- package/dist/src/tools/vtex/delete-product-specification.js.map +7 -0
- package/dist/src/tools/vtex/delete-sku-price.js +55 -0
- package/dist/src/tools/vtex/delete-sku-price.js.map +7 -0
- package/dist/src/tools/vtex/delete-sku-specification.js +9 -0
- package/dist/src/tools/vtex/delete-sku-specification.js.map +7 -0
- package/dist/src/tools/vtex/get-brand.js +6 -0
- package/dist/src/tools/vtex/get-brand.js.map +7 -0
- package/dist/src/tools/vtex/get-category-tree.js +6 -0
- package/dist/src/tools/vtex/get-category-tree.js.map +7 -0
- package/dist/src/tools/vtex/get-category.js +6 -0
- package/dist/src/tools/vtex/get-category.js.map +7 -0
- package/dist/src/tools/vtex/get-product-specifications.js +9 -0
- package/dist/src/tools/vtex/get-product-specifications.js.map +7 -0
- package/dist/src/tools/vtex/get-product.js +6 -0
- package/dist/src/tools/vtex/get-product.js.map +7 -0
- package/dist/src/tools/vtex/get-sku.js +6 -0
- package/dist/src/tools/vtex/get-sku.js.map +7 -0
- package/dist/src/tools/vtex/index.js +23 -1
- package/dist/src/tools/vtex/index.js.map +2 -2
- package/dist/src/tools/vtex/list-brands.js +6 -0
- package/dist/src/tools/vtex/list-brands.js.map +7 -0
- package/dist/src/tools/vtex/list-categories.js +6 -0
- package/dist/src/tools/vtex/list-categories.js.map +7 -0
- package/dist/src/tools/vtex/list-products-by-category.js +6 -0
- package/dist/src/tools/vtex/list-products-by-category.js.map +7 -0
- package/dist/src/tools/vtex/list-products.js +6 -0
- package/dist/src/tools/vtex/list-products.js.map +7 -0
- package/dist/src/tools/vtex/list-skus-by-product.js +6 -0
- package/dist/src/tools/vtex/list-skus-by-product.js.map +7 -0
- package/dist/src/tools/vtex/list-specification-groups.js +9 -0
- package/dist/src/tools/vtex/list-specification-groups.js.map +7 -0
- package/dist/src/tools/vtex/move-category.js +6 -0
- package/dist/src/tools/vtex/move-category.js.map +7 -0
- package/dist/src/tools/vtex/profile-resolution.js +4 -51
- package/dist/src/tools/vtex/profile-resolution.js.map +2 -2
- package/dist/src/tools/vtex/update-brand.js +6 -0
- package/dist/src/tools/vtex/update-brand.js.map +7 -0
- package/dist/src/tools/vtex/update-category.js +6 -0
- package/dist/src/tools/vtex/update-category.js.map +7 -0
- package/dist/src/tools/vtex/update-product-basic-fields.js +3 -65
- package/dist/src/tools/vtex/update-product-basic-fields.js.map +2 -2
- package/dist/src/tools/vtex/update-sku-basic-fields.js +1 -87
- package/dist/src/tools/vtex/update-sku-basic-fields.js.map +2 -2
- package/dist/src/tools/vtex/update-sku-price.js +21 -1
- package/dist/src/tools/vtex/update-sku-price.js.map +2 -2
- package/dist/src/tools/vtex/write-helpers.js +104 -14
- package/dist/src/tools/vtex/write-helpers.js.map +2 -2
- package/dist/src/utils/provider-profile-selection.js +117 -0
- package/dist/src/utils/provider-profile-selection.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js +678 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js +564 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js +387 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js +368 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js +626 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js +480 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js.map +7 -0
- package/dist/tests/setup.js +2 -0
- package/dist/tests/setup.js.map +7 -0
- package/dist/tests/smoke/test-harness.test.js +7 -0
- package/dist/tests/smoke/test-harness.test.js.map +7 -0
- package/dist/tests/vtex/read-only-utils.test.js +161 -0
- package/dist/tests/vtex/read-only-utils.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog-admin-docs.test.js +24 -0
- package/dist/tests/vtex/vtex-catalog-admin-docs.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog-admin-surface.test.js +30 -0
- package/dist/tests/vtex/vtex-catalog-admin-surface.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog-write-tools.test.js +712 -0
- package/dist/tests/vtex/vtex-catalog-write-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog.service.test.js +51 -0
- package/dist/tests/vtex/vtex-catalog.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-inventory-tools.test.js +201 -0
- package/dist/tests/vtex/vtex-inventory-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-logistics.service.test.js +134 -0
- package/dist/tests/vtex/vtex-logistics.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-order-details.tool.test.js +141 -0
- package/dist/tests/vtex/vtex-order-details.tool.test.js.map +7 -0
- package/dist/tests/vtex/vtex-order-write-tools.test.js +483 -0
- package/dist/tests/vtex/vtex-order-write-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-orders-summary.tool.test.js +185 -0
- package/dist/tests/vtex/vtex-orders-summary.tool.test.js.map +7 -0
- package/dist/tests/vtex/vtex-orders.service.test.js +120 -0
- package/dist/tests/vtex/vtex-orders.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-pricing-write-tools.test.js +202 -0
- package/dist/tests/vtex/vtex-pricing-write-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-pricing-write.service.test.js +106 -0
- package/dist/tests/vtex/vtex-pricing-write.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-pricing.service.test.js +88 -0
- package/dist/tests/vtex/vtex-pricing.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-small-tools.test.js +190 -0
- package/dist/tests/vtex/vtex-small-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-write-simple-tools.test.js +647 -0
- package/dist/tests/vtex/vtex-write-simple-tools.test.js.map +7 -0
- package/dist/vitest.config.js +15 -0
- package/dist/vitest.config.js.map +7 -0
- package/package.json +6 -2
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
const metaFetchMock = vi.fn();
|
|
3
|
+
vi.mock("../meta-api.js", () => ({
|
|
4
|
+
metaFetch: metaFetchMock
|
|
5
|
+
}));
|
|
6
|
+
const metaAdsModule = await import("../meta-ads.js");
|
|
7
|
+
describe("meta ads service", () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
vi.clearAllMocks();
|
|
10
|
+
});
|
|
11
|
+
it("lists businesses and ad accounts with normalized paging and account ids", async () => {
|
|
12
|
+
metaFetchMock.mockResolvedValueOnce({
|
|
13
|
+
data: [{ id: "biz-1", name: "Main Business", verification_status: "verified" }],
|
|
14
|
+
paging: {
|
|
15
|
+
next: "https://graph.facebook.com/v25.0/me/businesses?after=cursor-2"
|
|
16
|
+
}
|
|
17
|
+
}).mockResolvedValueOnce({
|
|
18
|
+
data: [
|
|
19
|
+
{
|
|
20
|
+
id: "act_123",
|
|
21
|
+
name: "Main Account",
|
|
22
|
+
currency: "ARS",
|
|
23
|
+
account_status: 1,
|
|
24
|
+
timezone_name: "America/Argentina/Buenos_Aires",
|
|
25
|
+
business: { id: "biz-1", name: "Main Business" }
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
paging: {
|
|
29
|
+
cursors: { before: "before-1", after: "after-1" },
|
|
30
|
+
next: "https://graph.facebook.com/v25.0/me/adaccounts?after=after-1"
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
await expect(
|
|
34
|
+
metaAdsModule.listAccessibleMetaBusinesses({ accessToken: "token" })
|
|
35
|
+
).resolves.toEqual({
|
|
36
|
+
businesses: [{ id: "biz-1", name: "Main Business", verification_status: "verified" }],
|
|
37
|
+
paging: { after: "cursor-2", before: void 0, has_more: true }
|
|
38
|
+
});
|
|
39
|
+
await expect(
|
|
40
|
+
metaAdsModule.listAccessibleMetaAdAccounts({ accessToken: "token" }, "after-0")
|
|
41
|
+
).resolves.toEqual({
|
|
42
|
+
adAccounts: [
|
|
43
|
+
{
|
|
44
|
+
id: "act_123",
|
|
45
|
+
account_id: "123",
|
|
46
|
+
name: "Main Account",
|
|
47
|
+
currency: "ARS",
|
|
48
|
+
account_status: 1,
|
|
49
|
+
timezone_name: "America/Argentina/Buenos_Aires",
|
|
50
|
+
business: { id: "biz-1", name: "Main Business" }
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
paging: { after: "after-1", before: "before-1", has_more: true }
|
|
54
|
+
});
|
|
55
|
+
expect(metaFetchMock).toHaveBeenNthCalledWith(
|
|
56
|
+
1,
|
|
57
|
+
{ accessToken: "token" },
|
|
58
|
+
"/me/businesses",
|
|
59
|
+
{ fields: "id,name,verification_status", limit: 100, after: void 0 }
|
|
60
|
+
);
|
|
61
|
+
expect(metaFetchMock).toHaveBeenNthCalledWith(
|
|
62
|
+
2,
|
|
63
|
+
{ accessToken: "token" },
|
|
64
|
+
"/me/adaccounts",
|
|
65
|
+
{
|
|
66
|
+
fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}",
|
|
67
|
+
limit: 100,
|
|
68
|
+
after: "after-0"
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
it("fetches individual objects and uses act_ account ids for account-scoped reads", async () => {
|
|
73
|
+
metaFetchMock.mockResolvedValueOnce({ id: "biz-1", name: "Business" }).mockResolvedValueOnce({ id: "act_321", name: "Scoped", currency: "USD" }).mockResolvedValueOnce({ data: [{ id: "cmp-1", name: "Campaign" }] }).mockResolvedValueOnce({ data: [{ id: "ad-1", name: "Ad", creative: { id: "crt-1" } }] }).mockResolvedValueOnce({ data: [{ spend: "10" }] }).mockResolvedValueOnce({ data: [{ publisher_platform: "instagram", spend: "4" }] });
|
|
74
|
+
await expect(metaAdsModule.getMetaBusinessInfo({ accessToken: "token" }, "biz-1")).resolves.toEqual({
|
|
75
|
+
id: "biz-1",
|
|
76
|
+
name: "Business"
|
|
77
|
+
});
|
|
78
|
+
await expect(metaAdsModule.getMetaAdAccountInfo({ accessToken: "token" }, "321")).resolves.toEqual({
|
|
79
|
+
id: "act_321",
|
|
80
|
+
account_id: "321",
|
|
81
|
+
name: "Scoped",
|
|
82
|
+
currency: "USD"
|
|
83
|
+
});
|
|
84
|
+
await expect(metaAdsModule.getMetaCampaigns({ accessToken: "token" }, "321", 10)).resolves.toEqual([
|
|
85
|
+
{ id: "cmp-1", name: "Campaign" }
|
|
86
|
+
]);
|
|
87
|
+
await expect(metaAdsModule.getMetaAds({ accessToken: "token" }, "321", 10)).resolves.toEqual([
|
|
88
|
+
{ id: "ad-1", name: "Ad", creative: { id: "crt-1" } }
|
|
89
|
+
]);
|
|
90
|
+
await expect(
|
|
91
|
+
metaAdsModule.getMetaInsights({ accessToken: "token" }, "act_321", { level: "account" })
|
|
92
|
+
).resolves.toEqual([{ spend: "10" }]);
|
|
93
|
+
await expect(
|
|
94
|
+
metaAdsModule.getMetaPlacementInsights({ accessToken: "token" }, "321", { level: "account" })
|
|
95
|
+
).resolves.toEqual([{ publisher_platform: "instagram", spend: "4" }]);
|
|
96
|
+
expect(metaFetchMock).toHaveBeenCalledWith(
|
|
97
|
+
{ accessToken: "token" },
|
|
98
|
+
"/act_321",
|
|
99
|
+
{
|
|
100
|
+
fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap"
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
expect(metaFetchMock).toHaveBeenCalledWith(
|
|
104
|
+
{ accessToken: "token" },
|
|
105
|
+
"/act_321/campaigns",
|
|
106
|
+
{ fields: "id,name,status,effective_status,objective", limit: 10 }
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
it("deduplicates account ids and ignores rejected account lookups", async () => {
|
|
110
|
+
metaFetchMock.mockResolvedValueOnce({ id: "act_111", name: "One", currency: "ARS" }).mockRejectedValueOnce(new Error("not found"));
|
|
111
|
+
await expect(
|
|
112
|
+
metaAdsModule.getMetaAdAccountsByIds({ accessToken: "token" }, ["act_111", "111", "222"])
|
|
113
|
+
).resolves.toEqual([{ id: "act_111", account_id: "111", name: "One", currency: "ARS" }]);
|
|
114
|
+
expect(metaFetchMock).toHaveBeenCalledTimes(2);
|
|
115
|
+
});
|
|
116
|
+
it("returns empty arrays when list responses omit data", async () => {
|
|
117
|
+
metaFetchMock.mockResolvedValue({});
|
|
118
|
+
await expect(metaAdsModule.getMetaInsights({ accessToken: "token" }, "obj-1", {})).resolves.toEqual([]);
|
|
119
|
+
await expect(metaAdsModule.getMetaCampaigns({ accessToken: "token" }, "123")).resolves.toEqual([]);
|
|
120
|
+
await expect(metaAdsModule.getMetaAds({ accessToken: "token" }, "123")).resolves.toEqual([]);
|
|
121
|
+
await expect(metaAdsModule.getMetaPlacementInsights({ accessToken: "token" }, "123", {})).resolves.toEqual(
|
|
122
|
+
[]
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
//# sourceMappingURL=meta-ads.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/services/meta/__tests__/meta-ads.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nconst metaFetchMock = vi.fn();\n\nvi.mock(\"../meta-api.js\", () => ({\n metaFetch: metaFetchMock,\n}));\n\nconst metaAdsModule = await import(\"../meta-ads.js\");\n\ndescribe(\"meta ads service\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"lists businesses and ad accounts with normalized paging and account ids\", async () => {\n metaFetchMock\n .mockResolvedValueOnce({\n data: [{ id: \"biz-1\", name: \"Main Business\", verification_status: \"verified\" }],\n paging: {\n next: \"https://graph.facebook.com/v25.0/me/businesses?after=cursor-2\",\n },\n })\n .mockResolvedValueOnce({\n data: [\n {\n id: \"act_123\",\n name: \"Main Account\",\n currency: \"ARS\",\n account_status: 1,\n timezone_name: \"America/Argentina/Buenos_Aires\",\n business: { id: \"biz-1\", name: \"Main Business\" },\n },\n ],\n paging: {\n cursors: { before: \"before-1\", after: \"after-1\" },\n next: \"https://graph.facebook.com/v25.0/me/adaccounts?after=after-1\",\n },\n });\n\n await expect(\n metaAdsModule.listAccessibleMetaBusinesses({ accessToken: \"token\" })\n ).resolves.toEqual({\n businesses: [{ id: \"biz-1\", name: \"Main Business\", verification_status: \"verified\" }],\n paging: { after: \"cursor-2\", before: undefined, has_more: true },\n });\n\n await expect(\n metaAdsModule.listAccessibleMetaAdAccounts({ accessToken: \"token\" }, \"after-0\")\n ).resolves.toEqual({\n adAccounts: [\n {\n id: \"act_123\",\n account_id: \"123\",\n name: \"Main Account\",\n currency: \"ARS\",\n account_status: 1,\n timezone_name: \"America/Argentina/Buenos_Aires\",\n business: { id: \"biz-1\", name: \"Main Business\" },\n },\n ],\n paging: { after: \"after-1\", before: \"before-1\", has_more: true },\n });\n\n expect(metaFetchMock).toHaveBeenNthCalledWith(\n 1,\n { accessToken: \"token\" },\n \"/me/businesses\",\n { fields: \"id,name,verification_status\", limit: 100, after: undefined }\n );\n expect(metaFetchMock).toHaveBeenNthCalledWith(\n 2,\n { accessToken: \"token\" },\n \"/me/adaccounts\",\n {\n fields:\n \"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}\",\n limit: 100,\n after: \"after-0\",\n }\n );\n });\n\n it(\"fetches individual objects and uses act_ account ids for account-scoped reads\", async () => {\n metaFetchMock\n .mockResolvedValueOnce({ id: \"biz-1\", name: \"Business\" })\n .mockResolvedValueOnce({ id: \"act_321\", name: \"Scoped\", currency: \"USD\" })\n .mockResolvedValueOnce({ data: [{ id: \"cmp-1\", name: \"Campaign\" }] })\n .mockResolvedValueOnce({ data: [{ id: \"ad-1\", name: \"Ad\", creative: { id: \"crt-1\" } }] })\n .mockResolvedValueOnce({ data: [{ spend: \"10\" }] })\n .mockResolvedValueOnce({ data: [{ publisher_platform: \"instagram\", spend: \"4\" }] });\n\n await expect(metaAdsModule.getMetaBusinessInfo({ accessToken: \"token\" }, \"biz-1\")).resolves.toEqual({\n id: \"biz-1\",\n name: \"Business\",\n });\n await expect(metaAdsModule.getMetaAdAccountInfo({ accessToken: \"token\" }, \"321\")).resolves.toEqual({\n id: \"act_321\",\n account_id: \"321\",\n name: \"Scoped\",\n currency: \"USD\",\n });\n await expect(metaAdsModule.getMetaCampaigns({ accessToken: \"token\" }, \"321\", 10)).resolves.toEqual([\n { id: \"cmp-1\", name: \"Campaign\" },\n ]);\n await expect(metaAdsModule.getMetaAds({ accessToken: \"token\" }, \"321\", 10)).resolves.toEqual([\n { id: \"ad-1\", name: \"Ad\", creative: { id: \"crt-1\" } },\n ]);\n await expect(\n metaAdsModule.getMetaInsights({ accessToken: \"token\" }, \"act_321\", { level: \"account\" })\n ).resolves.toEqual([{ spend: \"10\" }]);\n await expect(\n metaAdsModule.getMetaPlacementInsights({ accessToken: \"token\" }, \"321\", { level: \"account\" })\n ).resolves.toEqual([{ publisher_platform: \"instagram\", spend: \"4\" }]);\n\n expect(metaFetchMock).toHaveBeenCalledWith(\n { accessToken: \"token\" },\n \"/act_321\",\n {\n fields:\n \"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap\",\n }\n );\n expect(metaFetchMock).toHaveBeenCalledWith(\n { accessToken: \"token\" },\n \"/act_321/campaigns\",\n { fields: \"id,name,status,effective_status,objective\", limit: 10 }\n );\n });\n\n it(\"deduplicates account ids and ignores rejected account lookups\", async () => {\n metaFetchMock\n .mockResolvedValueOnce({ id: \"act_111\", name: \"One\", currency: \"ARS\" })\n .mockRejectedValueOnce(new Error(\"not found\"));\n\n await expect(\n metaAdsModule.getMetaAdAccountsByIds({ accessToken: \"token\" }, [\"act_111\", \"111\", \"222\"])\n ).resolves.toEqual([{ id: \"act_111\", account_id: \"111\", name: \"One\", currency: \"ARS\" }]);\n\n expect(metaFetchMock).toHaveBeenCalledTimes(2);\n });\n\n it(\"returns empty arrays when list responses omit data\", async () => {\n metaFetchMock.mockResolvedValue({});\n\n await expect(metaAdsModule.getMetaInsights({ accessToken: \"token\" }, \"obj-1\", {})).resolves.toEqual([]);\n await expect(metaAdsModule.getMetaCampaigns({ accessToken: \"token\" }, \"123\")).resolves.toEqual([]);\n await expect(metaAdsModule.getMetaAds({ accessToken: \"token\" }, \"123\")).resolves.toEqual([]);\n await expect(metaAdsModule.getMetaPlacementInsights({ accessToken: \"token\" }, \"123\", {})).resolves.toEqual(\n []\n );\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
const getMetaConfigMock = vi.fn();
|
|
3
|
+
const createMetaAppSecretProofMock = vi.fn();
|
|
4
|
+
vi.mock("../../../config/meta.js", () => ({
|
|
5
|
+
getMetaConfig: getMetaConfigMock,
|
|
6
|
+
createMetaAppSecretProof: createMetaAppSecretProofMock
|
|
7
|
+
}));
|
|
8
|
+
const { metaFetch } = await import("../meta-api.js");
|
|
9
|
+
describe("meta api", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
vi.clearAllMocks();
|
|
12
|
+
getMetaConfigMock.mockReturnValue({
|
|
13
|
+
baseUrl: "https://graph.facebook.com/v25.0",
|
|
14
|
+
appSecret: "app-secret"
|
|
15
|
+
});
|
|
16
|
+
createMetaAppSecretProofMock.mockReturnValue("proof-123");
|
|
17
|
+
});
|
|
18
|
+
it("builds Graph API URLs with token, proof and non-empty query params", async () => {
|
|
19
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
20
|
+
ok: true,
|
|
21
|
+
json: vi.fn().mockResolvedValue({ data: [{ id: "biz-1" }] })
|
|
22
|
+
});
|
|
23
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
24
|
+
const result = await metaFetch(
|
|
25
|
+
{ accessToken: "access-token" },
|
|
26
|
+
"me/businesses",
|
|
27
|
+
{
|
|
28
|
+
fields: "id,name",
|
|
29
|
+
limit: 100,
|
|
30
|
+
after: void 0,
|
|
31
|
+
empty: ""
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
expect(result).toEqual({ data: [{ id: "biz-1" }] });
|
|
35
|
+
expect(createMetaAppSecretProofMock).toHaveBeenCalledWith("access-token", "app-secret");
|
|
36
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
37
|
+
"https://graph.facebook.com/v25.0/me/businesses?access_token=access-token&appsecret_proof=proof-123&fields=id%2Cname&limit=100",
|
|
38
|
+
{
|
|
39
|
+
method: "GET",
|
|
40
|
+
headers: { Accept: "application/json" }
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
it("prefers user-facing Meta errors and falls back to status-based messages", async () => {
|
|
45
|
+
const userFacingResponse = {
|
|
46
|
+
ok: false,
|
|
47
|
+
status: 400,
|
|
48
|
+
json: vi.fn().mockResolvedValue({
|
|
49
|
+
error: {
|
|
50
|
+
error_user_msg: "Choose a different account.",
|
|
51
|
+
message: "Low-level error"
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
};
|
|
55
|
+
const statusFallbackResponse = {
|
|
56
|
+
ok: false,
|
|
57
|
+
status: 503,
|
|
58
|
+
json: vi.fn().mockRejectedValue(new Error("invalid json"))
|
|
59
|
+
};
|
|
60
|
+
const fetchMock = vi.fn().mockResolvedValueOnce(userFacingResponse).mockResolvedValueOnce(statusFallbackResponse);
|
|
61
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
62
|
+
await expect(metaFetch({ accessToken: "token" }, "/broken")).rejects.toThrow(
|
|
63
|
+
"Choose a different account."
|
|
64
|
+
);
|
|
65
|
+
await expect(metaFetch({ accessToken: "token" }, "/broken-again")).rejects.toThrow(
|
|
66
|
+
"Meta API request failed with status 503"
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=meta-api.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/services/meta/__tests__/meta-api.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nconst getMetaConfigMock = vi.fn();\nconst createMetaAppSecretProofMock = vi.fn();\n\nvi.mock(\"../../../config/meta.js\", () => ({\n getMetaConfig: getMetaConfigMock,\n createMetaAppSecretProof: createMetaAppSecretProofMock,\n}));\n\nconst { metaFetch } = await import(\"../meta-api.js\");\n\ndescribe(\"meta api\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n getMetaConfigMock.mockReturnValue({\n baseUrl: \"https://graph.facebook.com/v25.0\",\n appSecret: \"app-secret\",\n });\n createMetaAppSecretProofMock.mockReturnValue(\"proof-123\");\n });\n\n it(\"builds Graph API URLs with token, proof and non-empty query params\", async () => {\n const fetchMock = vi.fn().mockResolvedValue({\n ok: true,\n json: vi.fn().mockResolvedValue({ data: [{ id: \"biz-1\" }] }),\n });\n vi.stubGlobal(\"fetch\", fetchMock);\n\n const result = await metaFetch(\n { accessToken: \"access-token\" },\n \"me/businesses\",\n {\n fields: \"id,name\",\n limit: 100,\n after: undefined,\n empty: \"\",\n }\n );\n\n expect(result).toEqual({ data: [{ id: \"biz-1\" }] });\n expect(createMetaAppSecretProofMock).toHaveBeenCalledWith(\"access-token\", \"app-secret\");\n expect(fetchMock).toHaveBeenCalledWith(\n \"https://graph.facebook.com/v25.0/me/businesses?access_token=access-token&appsecret_proof=proof-123&fields=id%2Cname&limit=100\",\n {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n }\n );\n });\n\n it(\"prefers user-facing Meta errors and falls back to status-based messages\", async () => {\n const userFacingResponse = {\n ok: false,\n status: 400,\n json: vi.fn().mockResolvedValue({\n error: {\n error_user_msg: \"Choose a different account.\",\n message: \"Low-level error\",\n },\n }),\n };\n const statusFallbackResponse = {\n ok: false,\n status: 503,\n json: vi.fn().mockRejectedValue(new Error(\"invalid json\")),\n };\n const fetchMock = vi\n .fn()\n .mockResolvedValueOnce(userFacingResponse)\n .mockResolvedValueOnce(statusFallbackResponse);\n vi.stubGlobal(\"fetch\", fetchMock);\n\n await expect(metaFetch({ accessToken: \"token\" }, \"/broken\")).rejects.toThrow(\n \"Choose a different account.\"\n );\n await expect(metaFetch({ accessToken: \"token\" }, \"/broken-again\")).rejects.toThrow(\n \"Meta API request failed with status 503\"\n );\n });\n});\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY,UAAU,QAAQ,IAAI,UAAU;AAErD,MAAM,oBAAoB,GAAG,GAAG;AAChC,MAAM,+BAA+B,GAAG,GAAG;AAE3C,GAAG,KAAK,2BAA2B,OAAO;AAAA,EACxC,eAAe;AAAA,EACf,0BAA0B;AAC5B,EAAE;AAEF,MAAM,EAAE,UAAU,IAAI,MAAM,OAAO,gBAAgB;AAEnD,SAAS,YAAY,MAAM;AACzB,aAAW,MAAM;AACf,OAAG,cAAc;AACjB,sBAAkB,gBAAgB;AAAA,MAChC,SAAS;AAAA,MACT,WAAW;AAAA,IACb,CAAC;AACD,iCAA6B,gBAAgB,WAAW;AAAA,EAC1D,CAAC;AAED,KAAG,sEAAsE,YAAY;AACnF,UAAM,YAAY,GAAG,GAAG,EAAE,kBAAkB;AAAA,MAC1C,IAAI;AAAA,MACJ,MAAM,GAAG,GAAG,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,CAAC;AAAA,IAC7D,CAAC;AACD,OAAG,WAAW,SAAS,SAAS;AAEhC,UAAM,SAAS,MAAM;AAAA,MACnB,EAAE,aAAa,eAAe;AAAA,MAC9B;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,CAAC;AAClD,WAAO,4BAA4B,EAAE,qBAAqB,gBAAgB,YAAY;AACtF,WAAO,SAAS,EAAE;AAAA,MAChB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,QAAQ,mBAAmB;AAAA,MACxC;AAAA,IACF;AAAA,EACF,CAAC;AAED,KAAG,2EAA2E,YAAY;AACxF,UAAM,qBAAqB;AAAA,MACzB,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,MAAM,GAAG,GAAG,EAAE,kBAAkB;AAAA,QAC9B,OAAO;AAAA,UACL,gBAAgB;AAAA,UAChB,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,yBAAyB;AAAA,MAC7B,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,MAAM,GAAG,GAAG,EAAE,kBAAkB,IAAI,MAAM,cAAc,CAAC;AAAA,IAC3D;AACA,UAAM,YAAY,GACf,GAAG,EACH,sBAAsB,kBAAkB,EACxC,sBAAsB,sBAAsB;AAC/C,OAAG,WAAW,SAAS,SAAS;AAEhC,UAAM,OAAO,UAAU,EAAE,aAAa,QAAQ,GAAG,SAAS,CAAC,EAAE,QAAQ;AAAA,MACnE;AAAA,IACF;AACA,UAAM,OAAO,UAAU,EAAE,aAAa,QAAQ,GAAG,eAAe,CAAC,EAAE,QAAQ;AAAA,MACzE;AAAA,IACF;AAAA,EACF,CAAC;AACH,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
metaFetch
|
|
3
|
+
} from "./meta-api.js";
|
|
2
4
|
import { normalizeMetaAdAccountId, toActId } from "../../meta/meta-utils.js";
|
|
3
5
|
function decodePagingCursor(url) {
|
|
4
6
|
if (!url) {
|
|
@@ -10,12 +12,22 @@ function decodePagingCursor(url) {
|
|
|
10
12
|
return void 0;
|
|
11
13
|
}
|
|
12
14
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
function normalizeMetaAdAccount(account) {
|
|
16
|
+
return {
|
|
17
|
+
...account,
|
|
18
|
+
account_id: account.account_id || normalizeMetaAdAccountId(account.id || "")
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function listAccessibleMetaBusinesses(context, after) {
|
|
22
|
+
const response = await metaFetch(
|
|
23
|
+
context,
|
|
24
|
+
"/me/businesses",
|
|
25
|
+
{
|
|
26
|
+
fields: "id,name,verification_status",
|
|
27
|
+
limit: 100,
|
|
28
|
+
after
|
|
29
|
+
}
|
|
30
|
+
);
|
|
19
31
|
return {
|
|
20
32
|
businesses: response.data ?? [],
|
|
21
33
|
paging: {
|
|
@@ -25,16 +37,22 @@ async function listAccessibleMetaBusinesses(after) {
|
|
|
25
37
|
}
|
|
26
38
|
};
|
|
27
39
|
}
|
|
28
|
-
async function
|
|
29
|
-
|
|
30
|
-
fields: "id,
|
|
31
|
-
limit: 100,
|
|
32
|
-
after
|
|
40
|
+
async function getMetaBusinessInfo(context, businessId) {
|
|
41
|
+
return metaFetch(context, `/${businessId}`, {
|
|
42
|
+
fields: "id,name,verification_status"
|
|
33
43
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
}
|
|
45
|
+
async function listAccessibleMetaAdAccounts(context, after) {
|
|
46
|
+
const response = await metaFetch(
|
|
47
|
+
context,
|
|
48
|
+
"/me/adaccounts",
|
|
49
|
+
{
|
|
50
|
+
fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}",
|
|
51
|
+
limit: 100,
|
|
52
|
+
after
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
const accounts = (response.data ?? []).map(normalizeMetaAdAccount);
|
|
38
56
|
return {
|
|
39
57
|
adAccounts: accounts,
|
|
40
58
|
paging: {
|
|
@@ -44,42 +62,78 @@ async function listAccessibleMetaAdAccounts(after) {
|
|
|
44
62
|
}
|
|
45
63
|
};
|
|
46
64
|
}
|
|
47
|
-
async function getMetaAdAccountInfo(adAccountId) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
async function getMetaAdAccountInfo(context, adAccountId) {
|
|
66
|
+
const account = await metaFetch(
|
|
67
|
+
context,
|
|
68
|
+
`/${toActId(adAccountId)}`,
|
|
69
|
+
{
|
|
70
|
+
fields: "id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap"
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
return normalizeMetaAdAccount(account);
|
|
51
74
|
}
|
|
52
|
-
async function
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
async function getMetaAdAccountsByIds(context, adAccountIds) {
|
|
76
|
+
const uniqueIds = Array.from(
|
|
77
|
+
new Set(
|
|
78
|
+
adAccountIds.map((adAccountId) => normalizeMetaAdAccountId(adAccountId))
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
const results = await Promise.allSettled(
|
|
82
|
+
uniqueIds.map((adAccountId) => getMetaAdAccountInfo(context, adAccountId))
|
|
83
|
+
);
|
|
84
|
+
return results.flatMap(
|
|
85
|
+
(result) => result.status === "fulfilled" ? [result.value] : []
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
async function getMetaInsights(context, objectId, params) {
|
|
89
|
+
const response = await metaFetch(
|
|
90
|
+
context,
|
|
91
|
+
`/${objectId}/insights`,
|
|
92
|
+
{
|
|
93
|
+
fields: "impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas,date_start,date_stop",
|
|
94
|
+
...params
|
|
95
|
+
}
|
|
96
|
+
);
|
|
57
97
|
return response.data ?? [];
|
|
58
98
|
}
|
|
59
|
-
async function getMetaCampaigns(adAccountId, limit = 50) {
|
|
60
|
-
const response = await metaFetch(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
99
|
+
async function getMetaCampaigns(context, adAccountId, limit = 50) {
|
|
100
|
+
const response = await metaFetch(
|
|
101
|
+
context,
|
|
102
|
+
`/${toActId(adAccountId)}/campaigns`,
|
|
103
|
+
{
|
|
104
|
+
fields: "id,name,status,effective_status,objective",
|
|
105
|
+
limit
|
|
106
|
+
}
|
|
107
|
+
);
|
|
64
108
|
return response.data ?? [];
|
|
65
109
|
}
|
|
66
|
-
async function getMetaAds(adAccountId, limit = 50) {
|
|
67
|
-
const response = await metaFetch(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
110
|
+
async function getMetaAds(context, adAccountId, limit = 50) {
|
|
111
|
+
const response = await metaFetch(
|
|
112
|
+
context,
|
|
113
|
+
`/${toActId(adAccountId)}/ads`,
|
|
114
|
+
{
|
|
115
|
+
fields: "id,name,status,effective_status,creative{id}",
|
|
116
|
+
limit
|
|
117
|
+
}
|
|
118
|
+
);
|
|
71
119
|
return response.data ?? [];
|
|
72
120
|
}
|
|
73
|
-
async function getMetaPlacementInsights(adAccountId, params) {
|
|
74
|
-
const response = await metaFetch(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
121
|
+
async function getMetaPlacementInsights(context, adAccountId, params) {
|
|
122
|
+
const response = await metaFetch(
|
|
123
|
+
context,
|
|
124
|
+
`/${toActId(adAccountId)}/insights`,
|
|
125
|
+
{
|
|
126
|
+
fields: "impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas",
|
|
127
|
+
...params
|
|
128
|
+
}
|
|
129
|
+
);
|
|
78
130
|
return response.data ?? [];
|
|
79
131
|
}
|
|
80
132
|
export {
|
|
81
133
|
getMetaAdAccountInfo,
|
|
134
|
+
getMetaAdAccountsByIds,
|
|
82
135
|
getMetaAds,
|
|
136
|
+
getMetaBusinessInfo,
|
|
83
137
|
getMetaCampaigns,
|
|
84
138
|
getMetaInsights,
|
|
85
139
|
getMetaPlacementInsights,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/services/meta/meta-ads.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": "AAAA,
|
|
4
|
+
"sourcesContent": ["import {\n\tmetaFetch,\n\ttype MetaListResponse,\n\ttype MetaRequestContext,\n} from \"./meta-api.js\";\nimport { normalizeMetaAdAccountId, toActId } from \"../../meta/meta-utils.js\";\n\nexport interface MetaBusiness {\n\tid?: string;\n\tname?: string;\n\tverification_status?: string;\n}\n\nexport interface MetaBusinessRef {\n\tid?: string;\n\tname?: string;\n}\n\nexport interface MetaAdAccount {\n\tid?: string;\n\taccount_id?: string;\n\tname?: string;\n\tcurrency?: string;\n\taccount_status?: number;\n\ttimezone_name?: string;\n\ttimezone_offset_hours_utc?: number;\n\tbusiness?: MetaBusinessRef;\n}\n\nexport interface MetaActionMetric {\n\taction_type?: string;\n\tvalue?: string;\n}\n\nexport interface MetaRoasMetric {\n\taction_type?: string;\n\tvalue?: string;\n}\n\nexport interface MetaAdInsight {\n\tdate_start?: string;\n\tdate_stop?: string;\n\timpressions?: string;\n\treach?: string;\n\tclicks?: string;\n\tspend?: string;\n\tctr?: string;\n\tcpc?: string;\n\tcpm?: string;\n\tactions?: MetaActionMetric[];\n\taction_values?: MetaActionMetric[];\n\tpurchase_roas?: MetaRoasMetric[];\n}\n\nexport interface MetaCampaign {\n\tid?: string;\n\tname?: string;\n\tstatus?: string;\n\teffective_status?: string;\n\tobjective?: string;\n}\n\nexport interface MetaAd {\n\tid?: string;\n\tname?: string;\n\tstatus?: string;\n\teffective_status?: string;\n\tcreative?: {\n\t\tid?: string;\n\t};\n}\n\nexport interface MetaPlacementRow extends MetaAdInsight {\n\tpublisher_platform?: string;\n\tplatform_position?: string;\n\timpression_device?: string;\n}\n\nfunction decodePagingCursor(url: string | undefined): string | undefined {\n\tif (!url) {\n\t\treturn undefined;\n\t}\n\n\ttry {\n\t\treturn new URL(url).searchParams.get(\"after\") || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction normalizeMetaAdAccount(account: MetaAdAccount): MetaAdAccount {\n\treturn {\n\t\t...account,\n\t\taccount_id:\n\t\t\taccount.account_id || normalizeMetaAdAccountId(account.id || \"\"),\n\t};\n}\n\nexport async function listAccessibleMetaBusinesses(\n\tcontext: MetaRequestContext,\n\tafter?: string,\n) {\n\tconst response = await metaFetch<MetaListResponse<MetaBusiness>>(\n\t\tcontext,\n\t\t\"/me/businesses\",\n\t\t{\n\t\t\tfields: \"id,name,verification_status\",\n\t\t\tlimit: 100,\n\t\t\tafter,\n\t\t},\n\t);\n\n\treturn {\n\t\tbusinesses: response.data ?? [],\n\t\tpaging: {\n\t\t\tafter:\n\t\t\t\tresponse.paging?.cursors?.after ||\n\t\t\t\tdecodePagingCursor(response.paging?.next),\n\t\t\tbefore: response.paging?.cursors?.before,\n\t\t\thas_more: Boolean(response.paging?.next),\n\t\t},\n\t};\n}\n\nexport async function getMetaBusinessInfo(\n\tcontext: MetaRequestContext,\n\tbusinessId: string,\n): Promise<MetaBusiness> {\n\treturn metaFetch<MetaBusiness>(context, `/${businessId}`, {\n\t\tfields: \"id,name,verification_status\",\n\t});\n}\n\nexport async function listAccessibleMetaAdAccounts(\n\tcontext: MetaRequestContext,\n\tafter?: string,\n) {\n\tconst response = await metaFetch<MetaListResponse<MetaAdAccount>>(\n\t\tcontext,\n\t\t\"/me/adaccounts\",\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name}\",\n\t\t\tlimit: 100,\n\t\t\tafter,\n\t\t},\n\t);\n\n\tconst accounts = (response.data ?? []).map(normalizeMetaAdAccount);\n\n\treturn {\n\t\tadAccounts: accounts,\n\t\tpaging: {\n\t\t\tafter:\n\t\t\t\tresponse.paging?.cursors?.after ||\n\t\t\t\tdecodePagingCursor(response.paging?.next),\n\t\t\tbefore: response.paging?.cursors?.before,\n\t\t\thas_more: Boolean(response.paging?.next),\n\t\t},\n\t};\n}\n\nexport async function getMetaAdAccountInfo(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n): Promise<MetaAdAccount> {\n\tconst account = await metaFetch<MetaAdAccount>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}`,\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"id,account_id,name,currency,account_status,timezone_name,timezone_offset_hours_utc,business{id,name},amount_spent,balance,spend_cap\",\n\t\t},\n\t);\n\n\treturn normalizeMetaAdAccount(account);\n}\n\nexport async function getMetaAdAccountsByIds(\n\tcontext: MetaRequestContext,\n\tadAccountIds: string[],\n): Promise<MetaAdAccount[]> {\n\tconst uniqueIds = Array.from(\n\t\tnew Set(\n\t\t\tadAccountIds.map((adAccountId) => normalizeMetaAdAccountId(adAccountId)),\n\t\t),\n\t);\n\tconst results = await Promise.allSettled(\n\t\tuniqueIds.map((adAccountId) => getMetaAdAccountInfo(context, adAccountId)),\n\t);\n\n\treturn results.flatMap((result) =>\n\t\tresult.status === \"fulfilled\" ? [result.value] : [],\n\t);\n}\n\nexport async function getMetaInsights(\n\tcontext: MetaRequestContext,\n\tobjectId: string,\n\tparams: Record<string, string | number | undefined>,\n): Promise<MetaAdInsight[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaAdInsight>>(\n\t\tcontext,\n\t\t`/${objectId}/insights`,\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas,date_start,date_stop\",\n\t\t\t...params,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n\nexport async function getMetaCampaigns(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n\tlimit = 50,\n): Promise<MetaCampaign[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaCampaign>>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}/campaigns`,\n\t\t{\n\t\t\tfields: \"id,name,status,effective_status,objective\",\n\t\t\tlimit,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n\nexport async function getMetaAds(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n\tlimit = 50,\n): Promise<MetaAd[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaAd>>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}/ads`,\n\t\t{\n\t\t\tfields: \"id,name,status,effective_status,creative{id}\",\n\t\t\tlimit,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n\nexport async function getMetaPlacementInsights(\n\tcontext: MetaRequestContext,\n\tadAccountId: string,\n\tparams: Record<string, string | number | undefined>,\n): Promise<MetaPlacementRow[]> {\n\tconst response = await metaFetch<MetaListResponse<MetaPlacementRow>>(\n\t\tcontext,\n\t\t`/${toActId(adAccountId)}/insights`,\n\t\t{\n\t\t\tfields:\n\t\t\t\t\"impressions,reach,clicks,spend,ctr,cpc,cpm,actions,action_values,purchase_roas\",\n\t\t\t...params,\n\t\t},\n\t);\n\n\treturn response.data ?? [];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { createMetaAppSecretProof, getMetaConfig } from "../../config/meta.js";
|
|
2
|
-
function buildUrl(path, query) {
|
|
2
|
+
function buildUrl(context, path, query) {
|
|
3
3
|
const config = getMetaConfig();
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
const appSecretProof = createMetaAppSecretProof(
|
|
5
|
+
context.accessToken,
|
|
6
|
+
config.appSecret
|
|
7
|
+
);
|
|
6
8
|
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
7
9
|
const searchParams = new URLSearchParams();
|
|
8
|
-
searchParams.set("access_token", accessToken);
|
|
10
|
+
searchParams.set("access_token", context.accessToken);
|
|
9
11
|
searchParams.set("appsecret_proof", appSecretProof);
|
|
10
12
|
for (const [key, value] of Object.entries(query)) {
|
|
11
13
|
if (value === void 0 || value === null || value === "") {
|
|
@@ -15,8 +17,8 @@ function buildUrl(path, query) {
|
|
|
15
17
|
}
|
|
16
18
|
return `${config.baseUrl}${normalizedPath}?${searchParams.toString()}`;
|
|
17
19
|
}
|
|
18
|
-
async function metaFetch(path, query = {}) {
|
|
19
|
-
const response = await fetch(buildUrl(path, query), {
|
|
20
|
+
async function metaFetch(context, path, query = {}) {
|
|
21
|
+
const response = await fetch(buildUrl(context, path, query), {
|
|
20
22
|
method: "GET",
|
|
21
23
|
headers: {
|
|
22
24
|
Accept: "application/json"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/services/meta/meta-api.ts"],
|
|
4
|
-
"sourcesContent": ["import { createMetaAppSecretProof, getMetaConfig } from \"../../config/meta.js\";\n\ninterface MetaErrorPayload {\n
|
|
5
|
-
"mappings": "AAAA,SAAS,0BAA0B,qBAAqB;
|
|
4
|
+
"sourcesContent": ["import { createMetaAppSecretProof, getMetaConfig } from \"../../config/meta.js\";\n\ninterface MetaErrorPayload {\n\terror?: {\n\t\tmessage?: string;\n\t\ttype?: string;\n\t\tcode?: number;\n\t\terror_subcode?: number;\n\t\terror_user_title?: string;\n\t\terror_user_msg?: string;\n\t\tfbtrace_id?: string;\n\t};\n}\n\nexport interface MetaRequestContext {\n\taccessToken: string;\n}\n\nexport interface MetaPagingCursor {\n\tbefore?: string;\n\tafter?: string;\n}\n\nexport interface MetaPaging {\n\tcursors?: MetaPagingCursor;\n\tnext?: string;\n\tprevious?: string;\n}\n\nexport interface MetaListResponse<T> {\n\tdata?: T[];\n\tpaging?: MetaPaging;\n}\n\nfunction buildUrl(\n\tcontext: MetaRequestContext,\n\tpath: string,\n\tquery: Record<string, string | number | undefined>,\n): string {\n\tconst config = getMetaConfig();\n\tconst appSecretProof = createMetaAppSecretProof(\n\t\tcontext.accessToken,\n\t\tconfig.appSecret,\n\t);\n\tconst normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n\tconst searchParams = new URLSearchParams();\n\n\tsearchParams.set(\"access_token\", context.accessToken);\n\tsearchParams.set(\"appsecret_proof\", appSecretProof);\n\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tif (value === undefined || value === null || value === \"\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tsearchParams.set(key, String(value));\n\t}\n\n\treturn `${config.baseUrl}${normalizedPath}?${searchParams.toString()}`;\n}\n\nexport async function metaFetch<T>(\n\tcontext: MetaRequestContext,\n\tpath: string,\n\tquery: Record<string, string | number | undefined> = {},\n): Promise<T> {\n\tconst response = await fetch(buildUrl(context, path, query), {\n\t\tmethod: \"GET\",\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t});\n\n\tif (!response.ok) {\n\t\tconst payload = (await response\n\t\t\t.json()\n\t\t\t.catch(() => ({}))) as MetaErrorPayload;\n\t\tconst message =\n\t\t\tpayload.error?.error_user_msg ||\n\t\t\tpayload.error?.error_user_title ||\n\t\t\tpayload.error?.message ||\n\t\t\t`Meta API request failed with status ${response.status}`;\n\n\t\tthrow new Error(message);\n\t}\n\n\treturn (await response.json()) as T;\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,0BAA0B,qBAAqB;AAkCxD,SAAS,SACR,SACA,MACA,OACS;AACT,QAAM,SAAS,cAAc;AAC7B,QAAM,iBAAiB;AAAA,IACtB,QAAQ;AAAA,IACR,OAAO;AAAA,EACR;AACA,QAAM,iBAAiB,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AAC7D,QAAM,eAAe,IAAI,gBAAgB;AAEzC,eAAa,IAAI,gBAAgB,QAAQ,WAAW;AACpD,eAAa,IAAI,mBAAmB,cAAc;AAElD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AAC1D;AAAA,IACD;AAEA,iBAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,EACpC;AAEA,SAAO,GAAG,OAAO,OAAO,GAAG,cAAc,IAAI,aAAa,SAAS,CAAC;AACrE;AAEA,eAAsB,UACrB,SACA,MACA,QAAqD,CAAC,GACzC;AACb,QAAM,WAAW,MAAM,MAAM,SAAS,SAAS,MAAM,KAAK,GAAG;AAAA,IAC5D,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,QAAQ;AAAA,IACT;AAAA,EACD,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AACjB,UAAM,UAAW,MAAM,SACrB,KAAK,EACL,MAAM,OAAO,CAAC,EAAE;AAClB,UAAM,UACL,QAAQ,OAAO,kBACf,QAAQ,OAAO,oBACf,QAAQ,OAAO,WACf,uCAAuC,SAAS,MAAM;AAEvD,UAAM,IAAI,MAAM,OAAO;AAAA,EACxB;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC7B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|