Skip to content

Commit

Permalink
fix: rss datasource mixed up
Browse files Browse the repository at this point in the history
  • Loading branch information
vitPinchuk committed May 23, 2024
1 parent 8ca3dd7 commit bb6c924
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 104 deletions.
33 changes: 33 additions & 0 deletions src/api/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,5 +416,38 @@ describe('Api', () => {
expect(result?.length).toEqual(1);
expect(result[0].fields.length).toEqual(12);
});

/**
* Source with different key count in item
*/
it('Should return null value if key doesn`t contain in element', async () => {
xmlResponse.data = `<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<item>
<title>
RSS Tutorial
</title>
<content:encoded><![CDATA[<h4>Test.</h4><figure><img alt="" src="" /></figure>]]></content:encoded>
</item>
<item>
<content:encoded><![CDATA[<h4 /><figure />]]></content:encoded>
<title>
RSS Tutorial 2
</title>
<tag>
RSS Tag 2
</tag>
</item>
</channel>
</rss>`;

const result = await api.getFeed({ refId: 'A', feedType: FeedTypeValue.ITEMS }, range);

const tagField = result[0].fields.find((elem) => elem.name === 'tag');

expect(result?.length).toEqual(1);
expect(tagField?.values).toEqual([null, 'RSS Tag 2']);
});
});
});
220 changes: 121 additions & 99 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { lastValueFrom } from 'rxjs';

import { ALWAYS_ARRAY, FeedTypeValue, ItemKey, MetaProperties } from '../constants';
import { DataItem, DataSourceOptions, FeedItems, Query } from '../types';
import { isDateBetweenRange, setItem } from '../utils';
import { createUniqueKeyObject, isDateBetweenRange, setItem } from '../utils';

/**
* API
Expand Down Expand Up @@ -120,10 +120,14 @@ export class Api {
}

/**
* Find all items
* Configure Items
* Take all the unique keys in all items
*/
const items: FeedItems = {};
const items: FeedItems = createUniqueKeyObject(channel.item);

/**
* Find all items
*/
channel.item.forEach((item: DataItem) => {
/**
* Filter by specified Date field
Expand All @@ -132,44 +136,51 @@ export class Api {
return;
}

Object.keys(item).forEach((key: string) => {
Object.keys(items).forEach((key: string) => {
let currentKey = key;
let value = item[key];

/**
* Parse Meta
* If Item doesn`t contain key set key with null value to avoid mixed up
*/
if (key === ItemKey.META && (value as Record<string, string>)['@_property'] === MetaProperties.OG_IMAGE) {
key = MetaProperties.OG_IMAGE;
value = (value as Record<string, string>)['@_content'];
}

/**
* Parse Guid
*/
if (key === ItemKey.GUID && (value as Record<string, string>)['#text']) {
value = (value as Record<string, string>)['#text'];
}

/**
* Parse Encoded content for H4 and first Image
*/
if (key === ItemKey.CONTENT_ENCODED) {
const h4 = value.toString().match(/<h4>(.*?)<\/h4>/);
const figure = value.toString().match(/<figure>(.*?)<\/figure>/);
if (!value) {
setItem(items, currentKey, null);
} else {
/**
* Parse Meta
*/
if (key === ItemKey.META && (value as Record<string, string>)['@_property'] === MetaProperties.OG_IMAGE) {
currentKey = MetaProperties.OG_IMAGE;
value = (value as Record<string, string>)['@_content'];
}

setItem(items, ItemKey.CONTENT_H4, h4?.length ? h4[1] : '');
/**
* Parse Guid
*/
if (key === ItemKey.GUID && (value as Record<string, string>)['#text']) {
value = (value as Record<string, string>)['#text'];
}

/**
* Extract image and source
* Parse Encoded content for H4 and first Image
*/
if (figure?.length) {
setItem(items, ItemKey.CONTENT_IMG, figure[1]);
const img = figure[1].match(/<img.+src\=(?:\"|\')(.+?)(?:\"|\')(?:.+?)\>/);
setItem(items, ItemKey.CONTENT_IMG_SRC, img?.length ? img[1] : '');
if (key === ItemKey.CONTENT_ENCODED) {
const h4 = value.toString().match(/<h4>(.*?)<\/h4>/);
const figure = value.toString().match(/<figure>(.*?)<\/figure>/);

setItem(items, ItemKey.CONTENT_H4, h4?.length ? h4[1] : '');

/**
* Extract image and source
*/
if (figure?.length) {
setItem(items, ItemKey.CONTENT_IMG, figure[1]);
const img = figure[1].match(/<img.+src\=(?:\"|\')(.+?)(?:\"|\')(?:.+?)\>/);
setItem(items, ItemKey.CONTENT_IMG_SRC, img?.length ? img[1] : '');
}
}
setItem(items, currentKey, value as string);
}

setItem(items, key, value as string);
});
});

Expand Down Expand Up @@ -230,10 +241,14 @@ export class Api {
}

/**
* Find all entries
* Configure entries
* Take all the unique keys in all entries
*/
const entries: FeedItems = {};
const entries: FeedItems = createUniqueKeyObject(feed.entry);

/**
* Find all entries
*/
feed.entry.forEach((entry: DataItem) => {
/**
* Filter by specified Date field
Expand All @@ -242,91 +257,98 @@ export class Api {
return;
}

Object.keys(entry).forEach((key: string) => {
Object.keys(entries).forEach((key: string) => {
let value = entry[key];

/**
* Link
* If Entry doesn`t contain key set key with null value to avoid mixed up
*/
if (key === ItemKey.LINK && (value as Record<string, string>)['@_href']) {
value = (value as Record<string, string>)['@_href'];
}

/**
* Content
*/
if (key === ItemKey.CONTENT && (value as Record<string, string>)['#text']) {
value = (value as Record<string, string>)['#text'];
}

/**
* Summary
*/
if (key === ItemKey.SUMMARY && (value as Record<string, string>)['#text']) {
value = (value as Record<string, string>)['#text'];
}

/**
* Author
*/
if (key === ItemKey.AUTHOR && (value as Record<string, string>)['name']) {
value = (value as Record<string, string>)['name'];
}
if (!value) {
setItem(entries, key, null);
} else {
/**
* Link
*/
if (key === ItemKey.LINK && (value as Record<string, string>)['@_href']) {
value = (value as Record<string, string>)['@_href'];
}

/**
* Thumbnail
*/
if (key === ItemKey.MEDIA_THUMBNAIL && (value as Record<string, string>)['@_url']) {
value = (value as Record<string, string>)['@_url'];
}
/**
* Content
*/
if (key === ItemKey.CONTENT && (value as Record<string, string>)['#text']) {
value = (value as Record<string, string>)['#text'];
}

/**
* Media Group
*/
if (key === ItemKey.MEDIA_GROUP) {
const mediaGroup: Record<string, unknown> = value as Record<string, unknown>;
/**
* Summary
*/
if (key === ItemKey.SUMMARY && (value as Record<string, string>)['#text']) {
value = (value as Record<string, string>)['#text'];
}

/**
* Thumbnail URL
* Author
*/
if (
mediaGroup[ItemKey.MEDIA_THUMBNAIL] &&
(mediaGroup[ItemKey.MEDIA_THUMBNAIL] as Record<string, unknown>)['@_url']
) {
setItem(
entries,
`${ItemKey.MEDIA_GROUP}:${ItemKey.MEDIA_THUMBNAIL}:url`,
(mediaGroup[ItemKey.MEDIA_THUMBNAIL] as Record<string, unknown>)['@_url'] as string
);
if (key === ItemKey.AUTHOR && (value as Record<string, string>)['name']) {
value = (value as Record<string, string>)['name'];
}

/**
* Content URL
* Thumbnail
*/
if (
mediaGroup[ItemKey.MEDIA_CONTENT] &&
(mediaGroup[ItemKey.MEDIA_CONTENT] as Record<string, unknown>)['@_url']
) {
setItem(
entries,
`${ItemKey.MEDIA_GROUP}:${ItemKey.MEDIA_CONTENT}:url`,
(mediaGroup[ItemKey.MEDIA_CONTENT] as Record<string, unknown>)['@_url'] as string
);
if (key === ItemKey.MEDIA_THUMBNAIL && (value as Record<string, string>)['@_url']) {
value = (value as Record<string, string>)['@_url'];
}

/**
* Description
* Media Group
*/
if (mediaGroup[ItemKey.MEDIA_DESCRIPTION]) {
setItem(
entries,
`${ItemKey.MEDIA_GROUP}:${ItemKey.MEDIA_DESCRIPTION}`,
mediaGroup[ItemKey.MEDIA_DESCRIPTION] as string
);
if (key === ItemKey.MEDIA_GROUP) {
const mediaGroup: Record<string, unknown> = value as Record<string, unknown>;

/**
* Thumbnail URL
*/
if (
mediaGroup[ItemKey.MEDIA_THUMBNAIL] &&
(mediaGroup[ItemKey.MEDIA_THUMBNAIL] as Record<string, unknown>)['@_url']
) {
setItem(
entries,
`${ItemKey.MEDIA_GROUP}:${ItemKey.MEDIA_THUMBNAIL}:url`,
(mediaGroup[ItemKey.MEDIA_THUMBNAIL] as Record<string, unknown>)['@_url'] as string
);
}

/**
* Content URL
*/
if (
mediaGroup[ItemKey.MEDIA_CONTENT] &&
(mediaGroup[ItemKey.MEDIA_CONTENT] as Record<string, unknown>)['@_url']
) {
setItem(
entries,
`${ItemKey.MEDIA_GROUP}:${ItemKey.MEDIA_CONTENT}:url`,
(mediaGroup[ItemKey.MEDIA_CONTENT] as Record<string, unknown>)['@_url'] as string
);
}

/**
* Description
*/
if (mediaGroup[ItemKey.MEDIA_DESCRIPTION]) {
setItem(
entries,
`${ItemKey.MEDIA_GROUP}:${ItemKey.MEDIA_DESCRIPTION}`,
mediaGroup[ItemKey.MEDIA_DESCRIPTION] as string
);
}
}
}

setItem(entries, key, value as string);
setItem(entries, key, value as string);
}
});
});

Expand Down
6 changes: 3 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ export interface DataSourceOptions extends DataSourceJsonData {
*/
export interface FeedItems {
/**
* Mapping of ID to an array of strings representing the items
* Mapping of ID to an array of strings or strings and null
*
* @type {Record<string, string[]>}
* @type {Record<string, (string | null)[]>}
*/
[id: string]: string[];
[id: string]: Array<string | null>;
}

/**
Expand Down
41 changes: 41 additions & 0 deletions src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { createUniqueKeyObject } from './utils';

/**
* createUniqueKeyObject
*/
describe('createUniqueKeyObject', () => {
it('Returns an empty object for an empty input array', () => {
const result = createUniqueKeyObject([]);
expect(result).toEqual({});
});

it('Creates a unique key object for a non-empty array', () => {
const items = [
{ name: 'John', age: 30 },
{ name: 'Jane', email: '[email protected]' },
{ name: 'Bob', age: 25, email: '[email protected]' },
];

const result = createUniqueKeyObject(items as any);
expect(result).toEqual({
name: [],
age: [],
email: [],
});
});

it('handles duplicate keys in the array', () => {
const items = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 35 },
{ name: 'John', email: '[email protected]' },
];

const result = createUniqueKeyObject(items as any);
expect(result).toEqual({
name: [],
age: [],
email: [],
});
});
});
Loading

0 comments on commit bb6c924

Please sign in to comment.