Skip to content

Commit

Permalink
refactor: improve api schema (#2156)
Browse files Browse the repository at this point in the history
* Update api-schema.yaml

* add userId to components

* Update api-schema.yaml

* add null support to issues component

* add kind and userId to session component

* add userId to multistream component

* make error optional

* remove required params from response

* add webhook request body

* temp: add type ignore

* Update webhook.ts

* Update api-schema.yaml

* Update [id].tsx

* final fixes/improvments

* for schemas

* update webhook payload

* fix: recordings playback IDs can be null

* fix: schema validation

* fix: schema default

* fix: added null for profiles

* fix: compile schemas

* fix: change asset responses to 201

* fix: change to nullable create/patch stream

* feat: added nullable to asset profiles field

* fix: merge latest

* fix: added back 200 response for create asset

* api: Handle null profiles appropriately

* api/schema: Add breakdownBy[] to /data/usage docs

* api: Delete the deprecated (now broken) /import API

Everyone should be using /upload/url now

* api/schema: Address PR comments

* api/schema: Fix schemas of upload APIs

* fix: isMobile oneof

---------

Co-authored-by: Chase Adams <[email protected]>
Co-authored-by: Victor Elias <[email protected]>
  • Loading branch information
3 people authored May 17, 2024
1 parent d1936e9 commit e8a29df
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 153 deletions.
16 changes: 13 additions & 3 deletions packages/api/src/compile-schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,26 @@ const data = _.merge({}, apiData, dbData);
const indexPath = path.resolve(validatorDir, "index.js");
write(indexPath, indexStr);

const typeDefinition = `export type InputCreatorId =
const creatorIdTypeDefinition = `export type InputCreatorId =
| {
type: "unverified";
value: string;
}
| string;`;

const playbackPolicyTypeDefinition = "export type PlaybackPolicy1 = null;";
const playbackPolicy2TypeDefinition =
"export type PlaybackPolicy2 = PlaybackPolicy | PlaybackPolicy1;";

let typeStr = types.join("\n\n");
const cleanedTypeStr = typeStr.split(typeDefinition).join("");
typeStr = `${cleanedTypeStr.trim()}\n\n${typeDefinition}`;
const cleanedTypeStr = typeStr
.split(creatorIdTypeDefinition)
.join("")
.split(playbackPolicyTypeDefinition)
.join("")
.split(playbackPolicy2TypeDefinition)
.join("");
typeStr = `${cleanedTypeStr.trim()}\n\n${creatorIdTypeDefinition}\n\n${playbackPolicyTypeDefinition}\n\n${playbackPolicy2TypeDefinition}`;

const typePath = path.resolve(schemaDir, "types.d.ts");
write(typePath, typeStr);
Expand Down
145 changes: 71 additions & 74 deletions packages/api/src/controllers/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -804,88 +804,85 @@ app.post(
}
);

const uploadWithUrlHandler: RequestHandler = async (req, res) => {
let { url, encryption, c2pa, profiles, targetSegmentSizeSecs } =
req.body as NewAssetFromUrlPayload;
if (!url) {
return res.status(422).json({
errors: [`Must provide a "url" field for the asset contents`],
});
}
if (encryption) {
if (encryption.encryptedKey) {
if (!isValidBase64(encryption.encryptedKey)) {
return res.status(422).json({
errors: [`"encryptedKey" must be valid base64`],
});
app.post(
"/upload/url",
authorizer({}),
validatePost("new-asset-from-url-payload"),
async (req, res) => {
let { url, encryption, c2pa, profiles, targetSegmentSizeSecs } =
req.body as NewAssetFromUrlPayload;
if (!url) {
return res.status(422).json({
errors: [`Must provide a "url" field for the asset contents`],
});
}
if (encryption) {
if (encryption.encryptedKey) {
if (!isValidBase64(encryption.encryptedKey)) {
return res.status(422).json({
errors: [`"encryptedKey" must be valid base64`],
});
}
}
}
}

const id = uuid();
const playbackId = await generateUniquePlaybackId(id);
const newAsset = await validateAssetPayload(req, id, playbackId, Date.now(), {
type: "url",
url,
encryption: assetEncryptionWithoutKey(encryption),
});
const dupAsset = await db.asset.findDuplicateUrlUpload(
url,
req.user.id,
req.project?.id
);
if (dupAsset) {
const [task] = await db.task.find({ outputAssetId: dupAsset.id });
if (!task.length) {
console.error("Found asset with no task", dupAsset);
// proceed as a regular new asset
} else {
// return the existing asset and task, as if created now, with a slightly
// different status code (200, not 201). Should be transparent to clients.
res.status(200).json({ asset: dupAsset, task: { id: task[0].id } });
return;
const id = uuid();
const playbackId = await generateUniquePlaybackId(id);
const newAsset = await validateAssetPayload(
req,
id,
playbackId,
Date.now(),
{
type: "url",
url,
encryption: assetEncryptionWithoutKey(encryption),
}
);
const dupAsset = await db.asset.findDuplicateUrlUpload(
url,
req.user.id,
req.project?.id
);
if (dupAsset) {
const [task] = await db.task.find({ outputAssetId: dupAsset.id });
if (!task.length) {
console.error("Found asset with no task", dupAsset);
// proceed as a regular new asset
} else {
// return the existing asset and task, as if created now, with a slightly
// different status code (200, not 201). Should be transparent to clients.
res.status(200).json({ asset: dupAsset, task: { id: task[0].id } });
return;
}
}
}

await ensureQueueCapacity(req.config, req.user.id);
await ensureQueueCapacity(req.config, req.user.id);

const asset = await createAsset(newAsset, req.queue);
const task = await req.taskScheduler.createAndScheduleTask(
"upload",
{
upload: {
url,
c2pa,
catalystPipelineStrategy: catalystPipelineStrategy(req),
encryption,
thumbnails: !(await isExperimentSubject(
"vod-thumbs-off",
req.user?.id
)),
...(profiles ? { profiles } : null), // avoid serializing null profiles on the task,
targetSegmentSizeSecs,
const asset = await createAsset(newAsset, req.queue);
const task = await req.taskScheduler.createAndScheduleTask(
"upload",
{
upload: {
url,
c2pa,
catalystPipelineStrategy: catalystPipelineStrategy(req),
encryption,
thumbnails: !(await isExperimentSubject(
"vod-thumbs-off",
req.user?.id
)),
...(profiles ? { profiles } : null), // avoid serializing null profiles on the task,
targetSegmentSizeSecs,
},
},
},
undefined,
asset
);

res.status(201);
res.json({ asset, task: { id: task.id } });
};
undefined,
asset
);

app.post(
"/upload/url",
authorizer({}),
validatePost("new-asset-from-url-payload"),
uploadWithUrlHandler
);
// TODO: Remove this at some point. Registered only for backward compatibility.
app.post(
"/import",
authorizer({}),
validatePost("new-asset-payload"),
uploadWithUrlHandler
res.status(201);
res.json({ asset, task: { id: task.id } });
}
);

app.post(
Expand Down
Loading

0 comments on commit e8a29df

Please sign in to comment.