@adobe/spacecat-shared-data-access 2.8.4 → 2.9.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [@adobe/spacecat-shared-data-access-v2.9.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v2.8.4...@adobe/spacecat-shared-data-access-v2.9.0) (2025-02-25)
2
+
3
+
4
+ ### Features
5
+
6
+ * import config schema ([#626](https://github.com/adobe/spacecat-shared/issues/626)) ([df147d8](https://github.com/adobe/spacecat-shared/commit/df147d8c8f83cc45f373b1d89823229afe035129))
7
+
1
8
  # [@adobe/spacecat-shared-data-access-v2.8.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v2.8.3...@adobe/spacecat-shared-data-access-v2.8.4) (2025-02-25)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-data-access",
3
- "version": "2.8.4",
3
+ "version": "2.9.0",
4
4
  "description": "Shared modules of the Spacecat Services - Data Access",
5
5
  "type": "module",
6
6
  "engines": {
@@ -12,13 +12,75 @@
12
12
 
13
13
  import Joi from 'joi';
14
14
 
15
+ export const IMPORT_TYPES = {
16
+ ORGANIC_KEYWORDS: 'organic-keywords',
17
+ ORGANIC_TRAFFIC: 'organic-traffic',
18
+ TOP_PAGES: 'top-pages',
19
+ };
20
+
21
+ export const IMPORT_DESTINATIONS = {
22
+ DEFAULT: 'default',
23
+ };
24
+
25
+ export const IMPORT_SOURCES = {
26
+ AHREFS: 'ahrefs',
27
+ GSC: 'google',
28
+ };
29
+
30
+ const IMPORT_BASE_KEYS = {
31
+ destinations: Joi.array().items(Joi.string().valid(IMPORT_DESTINATIONS.DEFAULT)).required(),
32
+ sources: Joi.array().items(Joi.string().valid(...Object.values(IMPORT_SOURCES))).required(),
33
+ enabled: Joi.boolean().required().default(true),
34
+ };
35
+
36
+ export const IMPORT_TYPE_SCHEMAS = {
37
+ [IMPORT_TYPES.ORGANIC_KEYWORDS]: Joi.object({
38
+ type: Joi.string().valid(IMPORT_TYPES.ORGANIC_KEYWORDS).required(),
39
+ ...IMPORT_BASE_KEYS,
40
+ pageUrl: Joi.string().uri(),
41
+ }),
42
+ [IMPORT_TYPES.ORGANIC_TRAFFIC]: Joi.object({
43
+ type: Joi.string().valid(IMPORT_TYPES.ORGANIC_TRAFFIC).required(),
44
+ ...IMPORT_BASE_KEYS,
45
+ }),
46
+ [IMPORT_TYPES.TOP_PAGES]: Joi.object({
47
+ type: Joi.string().valid(IMPORT_TYPES.TOP_PAGES).required(),
48
+ ...IMPORT_BASE_KEYS,
49
+ geo: Joi.string(),
50
+ }),
51
+ };
52
+
53
+ export const DEFAULT_IMPORT_CONFIGS = {
54
+ 'organic-keywords': {
55
+ type: 'organic-keywords',
56
+ destinations: ['default'],
57
+ sources: ['ahrefs'],
58
+ enabled: true,
59
+ },
60
+ 'organic-traffic': {
61
+ type: 'organic-traffic',
62
+ destinations: ['default'],
63
+ sources: ['ahrefs'],
64
+ enabled: true,
65
+ },
66
+ 'top-pages': {
67
+ type: 'top-pages',
68
+ destinations: ['default'],
69
+ sources: ['ahrefs'],
70
+ enabled: true,
71
+ geo: 'global',
72
+ },
73
+ };
74
+
15
75
  export const configSchema = Joi.object({
16
76
  slack: Joi.object({
17
77
  workspace: Joi.string(),
18
78
  channel: Joi.string(),
19
79
  invitedUserCount: Joi.number().integer().min(0),
20
80
  }),
21
- imports: Joi.array().items(Joi.object({ type: Joi.string() }).unknown(true)),
81
+ imports: Joi.array().items(
82
+ Joi.alternatives().try(...Object.values(IMPORT_TYPE_SCHEMAS)),
83
+ ),
22
84
  fetchConfig: Joi.object({
23
85
  headers: Joi.object().pattern(Joi.string(), Joi.string()),
24
86
  }).optional(),
@@ -136,6 +198,47 @@ export const Config = (data = {}) => {
136
198
  state.fetchConfig = fetchConfig;
137
199
  };
138
200
 
201
+ self.enableImport = (type, config = {}) => {
202
+ if (!IMPORT_TYPE_SCHEMAS[type]) {
203
+ throw new Error(`Unknown import type: ${type}`);
204
+ }
205
+
206
+ const defaultConfig = DEFAULT_IMPORT_CONFIGS[type];
207
+ const newConfig = {
208
+ ...defaultConfig, ...config, type, enabled: true,
209
+ };
210
+
211
+ // Validate the new config against its schema
212
+ const { error } = IMPORT_TYPE_SCHEMAS[type].validate(newConfig);
213
+ if (error) {
214
+ throw new Error(`Invalid import config: ${error.message}`);
215
+ }
216
+
217
+ state.imports = state.imports || [];
218
+ // Remove existing import of same type if present
219
+ state.imports = state.imports.filter((imp) => imp.type !== type);
220
+ state.imports.push(newConfig);
221
+
222
+ validateConfiguration(state);
223
+ };
224
+
225
+ self.disableImport = (type) => {
226
+ if (!state.imports) return;
227
+
228
+ state.imports = state.imports.map(
229
+ (imp) => (imp.type === type ? { ...imp, enabled: false } : imp),
230
+ );
231
+
232
+ validateConfiguration(state);
233
+ };
234
+
235
+ self.getImportConfig = (type) => state.imports?.find((imp) => imp.type === type);
236
+
237
+ self.isImportEnabled = (type) => {
238
+ const config = self.getImportConfig(type);
239
+ return config?.enabled ?? false;
240
+ };
241
+
139
242
  return Object.freeze(self);
140
243
  };
141
244
 
@@ -23,13 +23,95 @@ import type {
23
23
  SiteTopPage,
24
24
  } from '../index';
25
25
 
26
+ export type IMPORT_TYPES = {
27
+ readonly ORGANIC_KEYWORDS: 'organic-keywords';
28
+ readonly ORGANIC_TRAFFIC: 'organic-traffic';
29
+ readonly TOP_PAGES: 'top-pages';
30
+ };
31
+
32
+ export type IMPORT_DESTINATIONS = {
33
+ readonly DEFAULT: 'default';
34
+ };
35
+
36
+ export type IMPORT_SOURCES = {
37
+ readonly AHREFS: 'ahrefs';
38
+ readonly GSC: 'google';
39
+ };
40
+
41
+ export type ImportType = 'organic-keywords' | 'organic-traffic' | 'top-pages';
42
+ export type ImportDestination = 'default';
43
+ export type ImportSource = 'ahrefs' | 'google';
44
+
45
+ export interface ImportConfig {
46
+ type: ImportType;
47
+ destinations: ImportDestination[];
48
+ sources: ImportSource[];
49
+ enabled: boolean;
50
+ pageUrl?: string;
51
+ geo?: string;
52
+ }
53
+
54
+ export interface SiteConfig {
55
+ state: {
56
+ slack?: {
57
+ workspace?: string;
58
+ channel?: string;
59
+ invitedUserCount?: number;
60
+ };
61
+ imports?: ImportConfig[];
62
+ handlers?: Record<string, {
63
+ mentions?: Record<string, string[]>;
64
+ excludedURLs?: string[];
65
+ manualOverwrites?: Array<{
66
+ brokenTargetURL?: string;
67
+ targetURL?: string;
68
+ }>;
69
+ fixedURLs?: Array<{
70
+ brokenTargetURL?: string;
71
+ targetURL?: string;
72
+ }>;
73
+ includedURLs?: string[];
74
+ groupedURLs?: Array<{
75
+ name: string;
76
+ pattern: string;
77
+ }>;
78
+ latestMetrics?: {
79
+ pageViewsChange: number;
80
+ ctrChange: number;
81
+ projectedTrafficValue: number;
82
+ };
83
+ }>;
84
+ fetchConfig?: {
85
+ headers?: Record<string, string>;
86
+ };
87
+ };
88
+ getSlackConfig(): { workspace?: string; channel?: string; invitedUserCount?: number };
89
+ getImports(): ImportConfig[];
90
+ getImportConfig(type: ImportType): ImportConfig | undefined;
91
+ isImportEnabled(type: ImportType): boolean;
92
+ enableImport(type: ImportType, config?: Partial<ImportConfig>): void;
93
+ disableImport(type: ImportType): void;
94
+ getHandlers(): Record<string, object>;
95
+ getHandlerConfig(type: string): object;
96
+ getSlackMentions(type: string): string[] | undefined;
97
+ getExcludedURLs(type: string): string[] | undefined;
98
+ getManualOverwrites(type: string):
99
+ Array<{ brokenTargetURL?: string; targetURL?: string }> | undefined;
100
+ getFixedURLs(type: string): Array<{ brokenTargetURL?: string; targetURL?: string }> | undefined;
101
+ getIncludedURLs(type: string): string[] | undefined;
102
+ getGroupedURLs(type: string): Array<{ name: string; pattern: string }> | undefined;
103
+ getLatestMetrics(type: string):
104
+ { pageViewsChange: number; ctrChange: number; projectedTrafficValue: number } | undefined;
105
+ getFetchConfig(): { headers?: Record<string, string> } | undefined;
106
+ }
107
+
26
108
  export interface Site extends BaseModel {
27
109
  getAudits(): Promise<Audit>;
28
110
  getAuditsByAuditType(auditType: string): Promise<Audit>;
29
111
  getAuditsByAuditTypeAndAuditedAt(auditType: string, auditedAt: string): Promise<Audit>;
30
112
  getBaseURL(): string;
31
113
  getName(): string;
32
- getConfig(): object;
114
+ getConfig(): SiteConfig;
33
115
  getDeliveryType(): string;
34
116
  getExperiments(): Promise<Experiment[]>;
35
117
  getExperimentsByExpId(expId: string): Promise<Experiment[]>;