Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fetch more media data #38

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cosmicjs/sdk",
"version": "1.0.12",
"version": "1.0.13",
"description": "The official client module for Cosmic. This module helps you easily add dynamic content to your website or application using the Cosmic headless CMS.",
"keywords": [
"headlesscms",
Expand Down
2 changes: 2 additions & 0 deletions src/clients/bucket/lib/methodChaining.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export default class MethodChaining {
endpoint: string = '';

opts: any;

constructor(endpoint: string) {
this.endpoint = endpoint;
}
Expand Down
4 changes: 2 additions & 2 deletions src/clients/bucket/objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const objectsChainMethods = (
const endpoint = `${apiConfig.apiUrl}/buckets/${
bucketConfig.bucketSlug
}/objects?read_key=${bucketConfig.readKey}${encodedQueryParam(query)}`;
return new FindChaining(endpoint);
return new FindChaining(endpoint, bucketConfig);
},

findOne<T extends Record<string, unknown>>(query: NonEmptyObject<T>) {
Expand All @@ -26,7 +26,7 @@ export const objectsChainMethods = (
}/objects?read_key=${bucketConfig.readKey}&limit=1${encodedQueryParam(
query
)}`;
return new FindOneChaining(endpoint);
return new FindOneChaining(endpoint, bucketConfig);
},

async insertOne(data: GenericObject) {
Expand Down
30 changes: 30 additions & 0 deletions src/clients/bucket/objects/lib/chaining.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
import MethodChaining from '../../lib/methodChaining';

/**
* Options for fetching object data.
* @property {Object} media - Options for media objects.
* @property {string} media.props - Comma-separated list of additional properties to fetch for media objects.
* @typedef {Object} MediaType
* @property {string} all - All media properties.
* @property {string} id - The unique identifier of the media object.
* @property {string} name - The name of the media file.
* @property {string} original_name - The original name of the media file.
* @property {number} size - The size of the media file in bytes.
* @property {string} type - The MIME type of the media file.
* @property {string} bucket - The bucket identifier.
* @property {string} created_at - The creation date of the media object.
* @property {string} folder - The folder where the media is stored.
* @property {string} url - The URL of the media file.
* @property {string} imgix_url - The Imgix URL of the media file.
* @property {string} alt_text - The alternative text for the media.
*/
type OptionsType = {
media: {
props: string;
};
};
export default class Chaining extends MethodChaining {
depth(depth: number) {
this.endpoint += `&depth=${depth}`;
Expand All @@ -15,4 +38,11 @@ export default class Chaining extends MethodChaining {
this.endpoint += `&after=${after}`;
return this;
}

options(options: OptionsType) {
if (options) {
this.opts = options;
}
return this;
}
}
24 changes: 21 additions & 3 deletions src/clients/bucket/objects/lib/find.chaining.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { PromiseFnType } from '../../../../types/promise.types';
import { promiserTryCatchWrapper } from '../../../../utils/request.promiser';
import Chaining from './chaining';
import { addFullMediaData } from '../../../../utils/addFullMedia';
import { BucketConfig } from '../../../../types/config.types';
import { createBucketClient } from '../..';

export default class FindChaining extends Chaining {
private bucketConfig: BucketConfig;

constructor(endpoint: string, bucketConfig: BucketConfig) {
super(endpoint);
this.bucketConfig = bucketConfig;
}

limit(limit: number) {
this.endpoint += `&limit=${limit}`;
return this;
Expand All @@ -12,8 +22,16 @@ export default class FindChaining extends Chaining {
onFulfilled?: PromiseFnType<FulfilledResult>,
onRejected?: PromiseFnType<RejectedResult>
) {
await promiserTryCatchWrapper(this.endpoint, onRejected, (res) =>
onFulfilled?.(res)
);
await promiserTryCatchWrapper(this.endpoint, onRejected, async (res) => {
// eslint-disable-next-line no-underscore-dangle
if (this.opts && this.opts.media && res.objects) {
res.objects = await addFullMediaData(
res.objects,
createBucketClient(this.bucketConfig),
this.opts.media.props
);
}
onFulfilled?.(res);
});
}
}
25 changes: 21 additions & 4 deletions src/clients/bucket/objects/lib/findOne.chaining.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import { PromiseFnType } from '../../../../types/promise.types';
import { promiserTryCatchWrapper } from '../../../../utils/request.promiser';
import Chaining from './chaining';
import { addFullMediaData } from '../../../../utils/addFullMedia';
import { BucketConfig } from '../../../../types/config.types';
import { createBucketClient } from '../..';

export default class FindOneChaining extends Chaining {
private bucketConfig: BucketConfig;

constructor(endpoint: string, bucketConfig: BucketConfig) {
super(endpoint);
this.bucketConfig = bucketConfig;
}

async then<FulfilledResult = any, RejectedResult = never>(
onFulfilled?: PromiseFnType<FulfilledResult>,
onRejected?: PromiseFnType<RejectedResult>
) {
await promiserTryCatchWrapper(this.endpoint, onRejected, (res) => {
onFulfilled?.({
object: res.objects && res.objects.length ? res.objects[0] : null,
});
await promiserTryCatchWrapper(this.endpoint, onRejected, async (res) => {
let object = res.objects && res.objects.length ? res.objects[0] : null;
if (this.opts && this.opts.media && object) {
object = await addFullMediaData(
object,
createBucketClient(this.bucketConfig),
this.opts.media.props
);
}

onFulfilled?.({ object });
});
}
}
77 changes: 77 additions & 0 deletions src/utils/addFullMedia.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const fetchMediaData = async (
cosmic: any,
filenames: string[],
props: string
) => {
const query = {
name: { $in: filenames },
};
const { media } = await cosmic.media
.find(query)
.props(!props || props === 'all' ? '' : `name,url,imgix_url,${props}`);
return media;
};

const extractMediaFiles = (obj: any): string[] => {
const mediaFiles: string[] = [];
JSON.stringify(obj, (_, value) => {
if (value && typeof value === 'object') {
const url = value.imgix_url || value.url;
if (url) {
mediaFiles.push(url.split('/').pop().split('?')[0]);
}
}
return value;
});
return [...new Set(mediaFiles)];
};

const mapMediaDataToResponse = (
response: any,
mediaData: any[],
props: string
) => {
const mediaMap = new Map(mediaData.map((item) => [item.name, item]));

const addFullMedia = (obj: any) => {
if (obj && typeof obj === 'object') {
Object.keys(obj).forEach((key) => {
if (obj[key] && typeof obj[key] === 'object') {
const url = obj[key].imgix_url || obj[key].url;
if (url) {
const filename = url.split('/').pop().split('?')[0];
if (mediaMap.has(filename)) {
// eslint-disable-next-line no-param-reassign
if (!props.includes('name')) {
delete mediaMap.get(filename).name;
}
const newObj = { ...mediaMap.get(filename) };
Object.assign(obj[key], newObj);
}
}
addFullMedia(obj[key]);
}
});
}
};

addFullMedia(response);
};

const addFullMediaData = async (response: any, cosmic: any, props: string) => {
const processItem = async (item: any) => {
const mediaFiles = extractMediaFiles(item);
if (mediaFiles.length > 0) {
const mediaData = await fetchMediaData(cosmic, mediaFiles, props);
mapMediaDataToResponse(item, mediaData, props);
}
return item;
};

if (Array.isArray(response)) {
return Promise.all(response.map((item) => processItem(item)));
}
return processItem(response);
};

export { addFullMediaData };
Loading