Skip to content

Commit

Permalink
Merge pull request #2 from siriusnottin/develop
Browse files Browse the repository at this point in the history
feat: new MediaType support
  • Loading branch information
siriusnottin authored Apr 16, 2023
2 parents 314dbe4 + 67c5228 commit 1ed6d14
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 137 deletions.
45 changes: 45 additions & 0 deletions helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as coda from "@codahq/packs-sdk";

export const ApiUrl = "https://photoslibrary.googleapis.com/v1";

export async function getConnectionName(context: coda.ExecutionContext) {
let request: coda.FetchRequest = {
method: "GET",
url: "https://www.googleapis.com/oauth2/v1/userinfo",
headers: {
"Content-Type": "application/json",
},
};
let userResponse = await context.fetcher.fetch(request);
let user = userResponse.body;
return user.name as string;
}

export const MediasContentCategoriesList = {
Animals: "ANIMALS",
Fashion: "FASHION",
Landmarks: "LANDMARKS",
Receipts: "RECEIPTS",
Weddings: "WEDDINGS",
Arts: "ARTS",
Flowers: "FLOWERS",
Landscapes: "LANDSCAPES",
Screenshots: "SCREENSHOTS",
Whiteboards: "WHITEBOARDS",
Birthdays: "BIRTHDAYS",
Food: "FOOD",
Night: "NIGHT",
Selfies: "SELFIES",
Cityscapes: "CITYSCAPES",
Gardens: "GARDENS",
People: "PEOPLE",
Sport: "SPORT",
Crafts: "CRAFTS",
Holidays: "HOLIDAYS",
Performances: "PERFORMANCES",
Travel: "TRAVEL",
Documents: "DOCUMENTS",
Houses: "HOUSES",
Pets: "PETS",
Utility: "UTILITY"
}
167 changes: 30 additions & 137 deletions pack.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();
import * as helpers from "./helpers";
import * as params from "./params";
import * as schemas from "./schemas";

const ApiBaseUrl = "https://photoslibrary.googleapis.com/v1";
export const pack = coda.newPack();

pack.addNetworkDomain("googleapis.com");

Expand All @@ -17,112 +19,24 @@ pack.setUserAuthentication({
access_type: "offline",
prompt: "consent",
},

// Determines the display name of the connected account.
getConnectionName: async function (context) {
let response = await context.fetcher.fetch({
method: "GET",
url: "https://www.googleapis.com/oauth2/v1/userinfo",
});
let user = response.body;
return user.name;
},
});

const MediaSchema = coda.makeObjectSchema({
properties: {
mediaId: {
type: coda.ValueType.String,
fromKey: "id",
required: true
},
filename: { type: coda.ValueType.String, required: true },
description: { type: coda.ValueType.String },
creationTime: {
type: coda.ValueType.String,
codaType: coda.ValueHintType.DateTime
},
width: { type: coda.ValueType.Number },
height: { type: coda.ValueType.Number },
image: {
type: coda.ValueType.String,
codaType: coda.ValueHintType.ImageReference,
},
url: {
type: coda.ValueType.String,
description: "Google Photos URL for the media.",
codaType: coda.ValueHintType.Url,
fromKey: "productUrl",
},
},
displayProperty: "filename",
idProperty: "mediaId",
featuredProperties: [
"image"
],
});

const MediaDateRangeParam = coda.makeParameter({
type: coda.ParameterType.DateArray,
name: "dateRange",
description: "The date range over which data should be fetched.",
suggestedValue: coda.PrecannedDateRange.LastWeek,
});

const MediasContentCategoriesList = {
Animals: "ANIMALS",
Fashion: "FASHION",
Landmarks: "LANDMARKS",
Receipts: "RECEIPTS",
Weddings: "WEDDINGS",
Arts: "ARTS",
Flowers: "FLOWERS",
Landscapes: "LANDSCAPES",
Screenshots: "SCREENSHOTS",
Whiteboards: "WHITEBOARDS",
Birthdays: "BIRTHDAYS",
Food: "FOOD",
Night: "NIGHT",
Selfies: "SELFIES",
Cityscapes: "CITYSCAPES",
Gardens: "GARDENS",
People: "PEOPLE",
Sport: "SPORT",
Crafts: "CRAFTS",
Holidays: "HOLIDAYS",
Performances: "PERFORMANCES",
Travel: "TRAVEL",
Documents: "DOCUMENTS",
Houses: "HOUSES",
Pets: "PETS",
Utility: "UTILITY"
}

const MediaCategoriesParam = coda.makeParameter({
type: coda.ParameterType.StringArray,
name: "categories",
description: "Filter by medias categories.",
optional: true,
autocomplete: Object.keys(MediasContentCategoriesList)
});

const MediaFavoritesParam = coda.makeParameter({
type: coda.ParameterType.Boolean,
name: "favorite",
description: "Filter by favorites medias.",
optional: true,
getConnectionName: helpers.getConnectionName,
});

pack.addSyncTable({
name: "Medias",
schema: MediaSchema,
schema: schemas.MediaSchema,
identityName: "Media",
formula: {
name: "SyncMedias",
description: "Sync medias from the user's library.",
parameters: [MediaDateRangeParam, MediaCategoriesParam, MediaFavoritesParam],
execute: async function ([dateRange, categories, favorite], context) {
let url = `${ApiBaseUrl}/mediaItems:search`;
parameters: [
params.MediaDateRangeParam,
params.MediaTypeParam,
params.MediaCategoriesIncludeParam,
params.MediaFavoritesParam
],
execute: async function ([dateRange, mediaType, categories, favorite], context) {
let url = `${helpers.ApiUrl}/mediaItems:search`;

function formatDate(date: Date, dateFormatter: Intl.DateTimeFormat) {
const dateParts = dateFormatter.formatToParts(date);
Expand Down Expand Up @@ -163,7 +77,7 @@ pack.addSyncTable({
includedContentCategories: string[],
};
};
pageToken?: string;
pageToken?: undefined | string;
};

let payload: RequestPayload = {
Expand All @@ -176,7 +90,7 @@ pack.addSyncTable({
}]
},
featureFilter: (favorite) ? { includedFeatures: ["FAVORITES"] } : undefined,
contentFilter: (categories) ? { includedContentCategories: categories.map(category => (MediasContentCategoriesList[category])) } : undefined,
contentFilter: (categories) ? { includedContentCategories: categories.map(category => (helpers.MediasContentCategoriesList[category])) } : undefined,
},
pageToken: (context.sync.continuation?.nextPageToken) ? context.sync.continuation.nextPageToken : undefined,
}
Expand All @@ -191,10 +105,20 @@ pack.addSyncTable({
let items = response.body.mediaItems;
if (items && items.length > 0) {
for (let item of items) {
// the api returns item.mediaMetadata.photo and item.mediaMetadata.video, we want to have a single mediaType property.
item.mediaType = (item.mediaMetadata.photo) ? "Photo" : "Video";
item.creationTime = item.mediaMetadata.creationTime
item.width = item.mediaMetadata.width
item.height = item.mediaMetadata.height
item.image = item.baseUrl + "=w2048-h1024"
};
};
if (mediaType) {
items = items.filter(item => (item.mediaType === mediaType));
}
if (items && items.length > 0) {
for (let item of items) {
// We get the image only after we have filtered the items since it can become quite costly in ressources.
item.image = item.baseUrl + "=w2048-h1024"//TODO: add parameter for image sizes.
};
};
let continuation;
Expand All @@ -211,47 +135,16 @@ pack.addSyncTable({
},
});

const MediaReferenceSchema = coda.makeReferenceSchemaFromObjectSchema(MediaSchema, "Media");

const AlbumSchema = coda.makeObjectSchema({
properties: {
albumId: {
type: coda.ValueType.String,
fromKey: "id",
},
title: { type: coda.ValueType.String },
// medias: {
// type: coda.ValueType.Array,
// items: MediaReferenceSchema
// },
url: {
type: coda.ValueType.String,
description: "Google Photos URL for the album.",
codaType: coda.ValueHintType.Url,
fromKey: "productUrl",
},
coverPhoto: {
type: coda.ValueType.String,
codaType: coda.ValueHintType.ImageReference,
},
},
displayProperty: "title",
idProperty: "albumId",
featuredProperties: [
"coverPhoto"
]
});

pack.addSyncTable({
name: "Albums",
schema: AlbumSchema,
schema: schemas.AlbumSchema,
identityName: "Album",
formula: {
name: "SyncAlbums",
description: "Sync all albums.",
parameters: [],
execute: async function ([], context) {
let url = `${ApiBaseUrl}/albums`;
let url = `${helpers.ApiUrl}/albums`;

if (context.sync.continuation) {
url = coda.withQueryParams(url, { pageToken: context.sync.continuation })
Expand All @@ -270,7 +163,7 @@ pack.addSyncTable({
const Albums = await AlbumsResponse.body.albums;
for (const album of Albums) {
// we want to search for all medias in the current album.
// let url = coda.withQueryParams(`${ApiBaseUrl}/mediaItems:search`, { pageSize: 5 });
// let url = coda.withQueryParams(`${helpers.ApiUrl}/mediaItems:search`, { pageSize: 5 });
// let body = { albumId: album.id };
// let mediaItemsInAlbum = [];
// let mediaItemsNextPageToken;
Expand Down
34 changes: 34 additions & 0 deletions params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as coda from "@codahq/packs-sdk";
import * as helpers from "./helpers";
import { MediasContentCategoriesList } from "./helpers";

export const MediaDateRangeParam = coda.makeParameter({
type: coda.ParameterType.DateArray,
name: "dateRange",
description: "The date range over which data should be fetched.",
suggestedValue: coda.PrecannedDateRange.LastWeek,
});

export const MediaTypeParam = coda.makeParameter({
type: coda.ParameterType.String,
name: "mediaType",
description: "The type of media to fetch.",
autocomplete: ["Photo", "Video"],
optional: true,
});

export const MediaCategoriesIncludeParam = coda.makeParameter({
type: coda.ParameterType.StringArray,
name: "categories",
description: "Filter by medias categories.",
optional: true,
autocomplete: Object.keys(MediasContentCategoriesList)
});

export const MediaFavoritesParam = coda.makeParameter({
type: coda.ParameterType.Boolean,
name: "favorite",
description: "Filter by favorites medias.",
optional: true,
});

66 changes: 66 additions & 0 deletions schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as coda from "@codahq/packs-sdk";

export const MediaSchema = coda.makeObjectSchema({
properties: {
mediaId: {
type: coda.ValueType.String,
fromKey: "id",
required: true
},
filename: { type: coda.ValueType.String, required: true },
mediaType: { type: coda.ValueType.String },
description: { type: coda.ValueType.String },
creationTime: {
type: coda.ValueType.String,
codaType: coda.ValueHintType.DateTime
},
width: { type: coda.ValueType.Number },
height: { type: coda.ValueType.Number },
image: {
type: coda.ValueType.String,
codaType: coda.ValueHintType.ImageAttachment,
},
url: {
type: coda.ValueType.String,
description: "Google Photos URL for the media.",
codaType: coda.ValueHintType.Url,
fromKey: "productUrl",
},
},
displayProperty: "filename",
idProperty: "mediaId",
featuredProperties: [
"image"
],
});

export const MediaReferenceSchema = coda.makeReferenceSchemaFromObjectSchema(MediaSchema, "Media");

export const AlbumSchema = coda.makeObjectSchema({
properties: {
albumId: {
type: coda.ValueType.String,
fromKey: "id",
},
title: { type: coda.ValueType.String },
medias: {
type: coda.ValueType.Array,
items: MediaReferenceSchema
},
url: {
type: coda.ValueType.String,
description: "Google Photos URL for the album.",
codaType: coda.ValueHintType.Url,
fromKey: "productUrl",
},
coverPhoto: {
type: coda.ValueType.String,
codaType: coda.ValueHintType.ImageAttachment,
},
},
displayProperty: "title",
idProperty: "albumId",
featuredProperties: [
"coverPhoto"
]
});

0 comments on commit 1ed6d14

Please sign in to comment.