diff --git a/lib/types/media-types.js b/lib/types/media-types.js --- a/lib/types/media-types.js +++ b/lib/types/media-types.js @@ -1,13 +1,21 @@ // @flow +import t, { type TInterface, type TUnion } from 'tcomb'; + import type { Shape } from './core.js'; import { type Platform } from './device-types.js'; +import { tShape, tString, tID } from '../utils/validation-utils.js'; export type Dimensions = $ReadOnly<{ +height: number, +width: number, }>; +export const dimensionsValidator: TInterface<Dimensions> = tShape<Dimensions>({ + height: t.Number, + width: t.Number, +}); + export type MediaType = 'photo' | 'video'; export type EncryptedMediaType = 'encrypted_photo' | 'encrypted_video'; @@ -246,6 +254,31 @@ +duration: number, // seconds }; +export const mediaLibrarySelectionValidator: TUnion<MediaLibrarySelection> = + t.union([ + tShape({ + step: tString('photo_library'), + dimensions: dimensionsValidator, + filename: t.maybe(t.String), + uri: t.String, + mediaNativeID: t.maybe(t.String), + selectTime: t.Number, + sendTime: t.Number, + retries: t.Number, + }), + tShape({ + step: tString('video_library'), + dimensions: dimensionsValidator, + filename: t.maybe(t.String), + uri: t.String, + mediaNativeID: t.maybe(t.String), + selectTime: t.Number, + sendTime: t.Number, + retries: t.Number, + duration: t.Number, + }), + ]); + export type PhotoCapture = { +step: 'photo_capture', +time: number, // ms @@ -258,6 +291,19 @@ +retries: number, }; +export const photoCaptureValidator: TInterface<PhotoCapture> = + tShape<PhotoCapture>({ + step: tString('photo_capture'), + time: t.Number, + dimensions: dimensionsValidator, + filename: t.String, + uri: t.String, + captureTime: t.Number, + selectTime: t.Number, + sendTime: t.Number, + retries: t.Number, + }); + export type PhotoPaste = { +step: 'photo_paste', +dimensions: Dimensions, @@ -268,11 +314,28 @@ +retries: number, }; +export const photoPasteValidator: TInterface<PhotoPaste> = tShape<PhotoPaste>({ + step: tString('photo_paste'), + dimensions: dimensionsValidator, + filename: t.String, + uri: t.String, + selectTime: t.Number, + sendTime: t.Number, + retries: t.Number, +}); + export type NativeMediaSelection = | MediaLibrarySelection | PhotoCapture | PhotoPaste; +export const nativeMediaSelectionValidator: TUnion<NativeMediaSelection> = + t.union([ + mediaLibrarySelectionValidator, + photoCaptureValidator, + photoPasteValidator, + ]); + export type MediaMissionStep = | NativeMediaSelection | { @@ -560,6 +623,14 @@ +localMediaSelection?: NativeMediaSelection, }; +export const imageValidator: TInterface<Image> = tShape<Image>({ + id: tID, + uri: t.String, + type: tString('photo'), + dimensions: dimensionsValidator, + localMediaSelection: t.maybe(nativeMediaSelectionValidator), +}); + export type EncryptedImage = { +id: string, // a media URI for keyserver uploads / blob holder for Blob service uploads @@ -569,6 +640,15 @@ +dimensions: Dimensions, }; +export const encryptedImageValidator: TInterface<EncryptedImage> = + tShape<EncryptedImage>({ + id: tID, + holder: t.String, + encryptionKey: t.String, + type: tString('encrypted_photo'), + dimensions: dimensionsValidator, + }); + export type Video = { +id: string, +uri: string, @@ -581,6 +661,17 @@ +localMediaSelection?: NativeMediaSelection, }; +export const videoValidator: TInterface<Video> = tShape<Video>({ + id: tID, + uri: t.String, + type: tString('video'), + dimensions: dimensionsValidator, + loop: t.maybe(t.Boolean), + thumbnailID: tID, + thumbnailURI: t.String, + localMediaSelection: t.maybe(nativeMediaSelectionValidator), +}); + export type EncryptedVideo = { +id: string, // a media URI for keyserver uploads / blob holder for Blob service uploads @@ -594,4 +685,24 @@ +thumbnailEncryptionKey: string, }; +export const encryptedVideoValidator: TInterface<EncryptedVideo> = + tShape<EncryptedVideo>({ + id: tID, + holder: t.String, + encryptionKey: t.String, + type: tString('encrypted_video'), + dimensions: dimensionsValidator, + loop: t.maybe(t.Boolean), + thumbnailID: tID, + thumbnailHolder: t.String, + thumbnailEncryptionKey: t.String, + }); + export type Media = Image | Video | EncryptedImage | EncryptedVideo; + +export const mediaValidator: TUnion<Media> = t.union([ + imageValidator, + videoValidator, + encryptedImageValidator, + encryptedVideoValidator, +]); diff --git a/lib/types/validation.test.js b/lib/types/validation.test.js new file mode 100644 --- /dev/null +++ b/lib/types/validation.test.js @@ -0,0 +1,44 @@ +// @flow + +import { + imageValidator, + videoValidator, + mediaValidator, +} from './media-types.js'; + +describe('media validation', () => { + const photo = { + id: '92696', + type: 'photo', + uri: 'http://0.0.0.0:3000/comm/upload/92696/0fb272bd1c75d976', + dimensions: { + width: 340, + height: 288, + }, + }; + const video = { + type: 'video', + id: '92769', + uri: 'http://0.0.0.0:3000/comm/upload/92769/4bcc6987b25b2f66', + dimensions: { + width: 480, + height: 270, + }, + thumbnailID: '92770', + thumbnailURI: 'http://0.0.0.0:3000/comm/upload/92770/d56466051dcef1db', + }; + + it('should validate correct media', () => { + expect(mediaValidator.is(photo)).toBe(true); + expect(imageValidator.is(photo)).toBe(true); + expect(mediaValidator.is(video)).toBe(true); + expect(videoValidator.is(video)).toBe(true); + }); + + it('should not validate incorrect media', () => { + expect(imageValidator.is(video)).toBe(false); + expect(videoValidator.is(photo)).toBe(false); + expect(mediaValidator.is({ ...photo, type: undefined })).toBe(false); + expect(mediaValidator.is({ ...video, dimensions: undefined })).toBe(false); + }); +});