diff --git a/packages/api-elasticsearch/src/sort.ts b/packages/api-elasticsearch/src/sort.ts
index 7080c3bf069..ff32ecf6f2c 100644
--- a/packages/api-elasticsearch/src/sort.ts
+++ b/packages/api-elasticsearch/src/sort.ts
@@ -1,5 +1,5 @@
import WebinyError from "@webiny/error";
-import { FieldSortOptions, SortType, SortOrder } from "~/types";
+import { FieldSortOptions, SortOrder, SortType } from "~/types";
import { ElasticsearchFieldPlugin } from "~/plugins";
const sortRegExp = new RegExp(/^([a-zA-Z-0-9_@]+)_(ASC|DESC)$/);
@@ -31,7 +31,7 @@ export const createSort = (params: CreateSortParams): SortType => {
/**
* Cast as string because nothing else should be allowed yet.
*/
- return sort.reduce((acc, value) => {
+ const result = sort.reduce((acc, value) => {
if (typeof value !== "string") {
throw new WebinyError(`Sort as object is not supported..`);
}
@@ -61,4 +61,13 @@ export const createSort = (params: CreateSortParams): SortType => {
return acc;
}, {} as Record);
+ /**
+ * If we do not have id in the sort, we add it as we need a tie_breaker for the Elasticsearch to be able to sort consistently.
+ */
+ if (!result["id.keyword"] && !result["id"]) {
+ result["id.keyword"] = {
+ order: "asc"
+ };
+ }
+ return result;
};
diff --git a/packages/api-file-manager-s3/src/assetDelivery/assetDeliveryConfig.ts b/packages/api-file-manager-s3/src/assetDelivery/assetDeliveryConfig.ts
index a2cafb0c368..7847b083597 100644
--- a/packages/api-file-manager-s3/src/assetDelivery/assetDeliveryConfig.ts
+++ b/packages/api-file-manager-s3/src/assetDelivery/assetDeliveryConfig.ts
@@ -10,6 +10,7 @@ import { SharpTransform } from "~/assetDelivery/s3/SharpTransform";
export type AssetDeliveryParams = Parameters[0] & {
imageResizeWidths?: number[];
presignedUrlTtl?: number;
+ assetStreamingMaxSize?: number;
};
export const assetDeliveryConfig = (params: AssetDeliveryParams) => {
@@ -19,6 +20,11 @@ export const assetDeliveryConfig = (params: AssetDeliveryParams) => {
const {
presignedUrlTtl = 900,
imageResizeWidths = [100, 300, 500, 750, 1000, 1500, 2500],
+ /**
+ * Even though Lambda's response payload limit is 6,291,556 bytes, we leave some room for the response envelope.
+ * We had situations where a 4.7MB file would cause the payload to go over the limit, so let's be on the safe side.
+ */
+ assetStreamingMaxSize = 4718592,
...baseParams
} = params;
@@ -35,7 +41,7 @@ export const assetDeliveryConfig = (params: AssetDeliveryParams) => {
});
config.decorateAssetOutputStrategy(() => {
- return new S3OutputStrategy(s3, bucket, presignedUrlTtl);
+ return new S3OutputStrategy(s3, bucket, presignedUrlTtl, assetStreamingMaxSize);
});
config.decorateAssetTransformationStrategy(() => {
diff --git a/packages/api-file-manager-s3/src/assetDelivery/s3/S3OutputStrategy.ts b/packages/api-file-manager-s3/src/assetDelivery/s3/S3OutputStrategy.ts
index ea574101aed..894cfd33f85 100644
--- a/packages/api-file-manager-s3/src/assetDelivery/s3/S3OutputStrategy.ts
+++ b/packages/api-file-manager-s3/src/assetDelivery/s3/S3OutputStrategy.ts
@@ -3,8 +3,6 @@ import { GetObjectCommand, getSignedUrl, S3 } from "@webiny/aws-sdk/client-s3";
import { S3RedirectAssetReply } from "~/assetDelivery/s3/S3RedirectAssetReply";
import { S3StreamAssetReply } from "~/assetDelivery/s3/S3StreamAssetReply";
-const MAX_RETURN_CONTENT_LENGTH = 4915200; // ~4.8 MB
-
/**
* This strategy outputs an asset taking into account the size of the asset contents.
* If the asset is larger than 5MB, a presigned URL will be generated, and a redirect will happen.
@@ -13,21 +11,30 @@ export class S3OutputStrategy implements AssetOutputStrategy {
private readonly s3: S3;
private readonly bucket: string;
private readonly presignedUrlTtl: number;
+ private readonly assetStreamingMaxSize: number;
- constructor(s3: S3, bucket: string, presignedUrlTtl: number) {
+ constructor(s3: S3, bucket: string, presignedUrlTtl: number, assetStreamingMaxSize: number) {
+ this.assetStreamingMaxSize = assetStreamingMaxSize;
this.presignedUrlTtl = presignedUrlTtl;
this.s3 = s3;
this.bucket = bucket;
}
async output(asset: Asset): Promise {
- if ((await asset.getSize()) > MAX_RETURN_CONTENT_LENGTH) {
+ if (asset.getSize() > this.assetStreamingMaxSize) {
+ console.log(
+ `Asset size is greater than ${this.assetStreamingMaxSize}; redirecting to a presigned S3 URL.`
+ );
+
return new S3RedirectAssetReply(
await this.getPresignedUrl(asset),
this.presignedUrlTtl
);
}
+ console.log(
+ `Asset size is smaller than ${this.assetStreamingMaxSize}; streaming directly from Lambda function.`
+ );
return new S3StreamAssetReply(asset);
}
diff --git a/packages/api-file-manager-s3/src/assetDelivery/s3/SharpTransform.ts b/packages/api-file-manager-s3/src/assetDelivery/s3/SharpTransform.ts
index cb616787810..39eba04f637 100644
--- a/packages/api-file-manager-s3/src/assetDelivery/s3/SharpTransform.ts
+++ b/packages/api-file-manager-s3/src/assetDelivery/s3/SharpTransform.ts
@@ -26,6 +26,9 @@ export class SharpTransform implements AssetTransformationStrategy {
async transform(assetRequest: AssetRequest, asset: Asset): Promise {
if (!utils.SUPPORTED_TRANSFORMABLE_IMAGES.includes(asset.getExtension())) {
+ console.log(
+ `Transformations/optimizations of ${asset.getContentType()} assets are not supported. Skipping.`
+ );
return asset;
}
@@ -45,6 +48,7 @@ export class SharpTransform implements AssetTransformationStrategy {
}
private async transformAsset(asset: Asset, options: Omit) {
+ console.log("Transform asset", options);
if (options.width) {
const { s3, bucket } = this.params;
@@ -63,7 +67,15 @@ export class SharpTransform implements AssetTransformationStrategy {
const buffer = Buffer.from(await Body.transformToByteArray());
- asset.setContentsReader(new CallableContentsReader(() => buffer));
+ const newAsset = asset.withProps({ size: buffer.length });
+ newAsset.setContentsReader(new CallableContentsReader(() => buffer));
+
+ console.log(`Return a previously transformed asset`, {
+ key: transformedAssetKey,
+ size: newAsset.getSize()
+ });
+
+ return newAsset;
} catch (e) {
const optimizedImage = await this.optimizeAsset(asset);
@@ -73,22 +85,33 @@ export class SharpTransform implements AssetTransformationStrategy {
/**
* `width` is the only transformation we currently support.
*/
+ console.log(`Resize the asset (width: ${width})`);
const buffer = await optimizedImage.getContents();
- const transformedBuffer = sharp(buffer, { animated: this.isAssetAnimated(asset) })
+ const transformedBuffer = await sharp(buffer, {
+ animated: this.isAssetAnimated(asset)
+ })
.resize({ width })
.toBuffer();
/**
* Transformations are applied to the optimized image.
*/
- asset.setContentsReader(new CallableContentsReader(() => transformedBuffer));
+ const newAsset = asset.withProps({ size: transformedBuffer.length });
+ newAsset.setContentsReader(new CallableContentsReader(() => transformedBuffer));
await s3.putObject({
Bucket: bucket,
Key: transformedAssetKey,
- ContentType: asset.getContentType(),
- Body: await asset.getContents()
+ ContentType: newAsset.getContentType(),
+ Body: await newAsset.getContents()
});
+
+ console.log(`Return the resized asset`, {
+ key: transformedAssetKey,
+ size: newAsset.getSize()
+ });
+
+ return newAsset;
}
}
@@ -98,6 +121,13 @@ export class SharpTransform implements AssetTransformationStrategy {
private async optimizeAsset(asset: Asset) {
const { s3, bucket } = this.params;
+ console.log("Optimize asset", {
+ id: asset.getId(),
+ key: asset.getKey(),
+ size: asset.getSize(),
+ type: asset.getContentType()
+ });
+
const assetKey = new AssetKeyGenerator(asset);
const optimizedAssetKey = assetKey.getOptimizedImageKey();
@@ -111,10 +141,16 @@ export class SharpTransform implements AssetTransformationStrategy {
throw new Error(`Missing image body!`);
}
+ console.log("Return a previously optimized asset", optimizedAssetKey);
+
const buffer = Buffer.from(await Body.transformToByteArray());
- asset.setContentsReader(new CallableContentsReader(() => buffer));
+ const newAsset = asset.withProps({ size: buffer.length });
+ newAsset.setContentsReader(new CallableContentsReader(() => buffer));
+
+ return newAsset;
} catch (e) {
+ console.log("Create an optimized version of the original asset", asset.getKey());
// If not found, create an optimized version of the original asset.
const buffer = await asset.getContents();
@@ -127,23 +163,26 @@ export class SharpTransform implements AssetTransformationStrategy {
const optimization = optimizationMap[asset.getContentType()];
if (!optimization) {
- console.log(`no optimizations defined for ${asset.getContentType()}`);
+ console.log(`No optimizations defined for ${asset.getContentType()}`);
return asset;
}
- const optimizedBuffer = optimization(buffer).toBuffer();
+ const optimizedBuffer = await optimization(buffer).toBuffer();
+
+ console.log("Optimized asset size", optimizedBuffer.length);
- asset.setContentsReader(new CallableContentsReader(() => optimizedBuffer));
+ const newAsset = asset.withProps({ size: optimizedBuffer.length });
+ newAsset.setContentsReader(new CallableContentsReader(() => optimizedBuffer));
await s3.putObject({
Bucket: bucket,
Key: optimizedAssetKey,
- ContentType: asset.getContentType(),
- Body: await asset.getContents()
+ ContentType: newAsset.getContentType(),
+ Body: await newAsset.getContents()
});
- }
- return asset;
+ return newAsset;
+ }
}
private isAssetAnimated(asset: Asset) {
@@ -160,6 +199,7 @@ export class SharpTransform implements AssetTransformationStrategy {
private optimizeJpeg(buffer: Buffer) {
return sharp(buffer)
.resize({ width: 2560, withoutEnlargement: true, fit: "inside" })
+ .withMetadata()
.toFormat("jpeg", { quality: 90 });
}
}
diff --git a/packages/api-file-manager/src/delivery/AssetDelivery/Asset.ts b/packages/api-file-manager/src/delivery/AssetDelivery/Asset.ts
index ed209ee8470..44a2daf379a 100644
--- a/packages/api-file-manager/src/delivery/AssetDelivery/Asset.ts
+++ b/packages/api-file-manager/src/delivery/AssetDelivery/Asset.ts
@@ -21,10 +21,14 @@ export class Asset {
}
clone() {
- const clonedAsset = new Asset(structuredClone(this.props));
- clonedAsset.outputStrategy = this.outputStrategy;
- clonedAsset.contentsReader = this.contentsReader;
- return clonedAsset;
+ return this.withProps(structuredClone(this.props));
+ }
+
+ withProps(props: Partial) {
+ const newAsset = new Asset({ ...this.props, ...props });
+ newAsset.contentsReader = this.contentsReader;
+ newAsset.outputStrategy = this.outputStrategy;
+ return newAsset;
}
getId() {
@@ -39,9 +43,8 @@ export class Asset {
getKey() {
return this.props.key;
}
- async getSize() {
- const buffer = await this.getContents();
- return buffer.length;
+ getSize() {
+ return this.props.size;
}
getContentType() {
return this.props.contentType;
diff --git a/packages/api-file-manager/src/delivery/AssetDelivery/FilesAssetRequestResolver.ts b/packages/api-file-manager/src/delivery/AssetDelivery/FilesAssetRequestResolver.ts
index b0b17f62685..5ef161179f8 100644
--- a/packages/api-file-manager/src/delivery/AssetDelivery/FilesAssetRequestResolver.ts
+++ b/packages/api-file-manager/src/delivery/AssetDelivery/FilesAssetRequestResolver.ts
@@ -1,6 +1,6 @@
import { Request } from "@webiny/handler/types";
import { AssetRequestResolver } from "./abstractions/AssetRequestResolver";
-import { AssetRequest } from "./AssetRequest";
+import { AssetRequest, AssetRequestOptions } from "./AssetRequest";
export class FilesAssetRequestResolver implements AssetRequestResolver {
async resolve(request: Request): Promise {
@@ -15,15 +15,21 @@ export class FilesAssetRequestResolver implements AssetRequestResolver {
// Example: { '*': '/files/65722cb5c7824a0008d05963/image-48.jpg' },
const path = params["*"];
+ const options: AssetRequestOptions = {
+ ...query,
+ original: "original" in query
+ };
+
+ if (query.width) {
+ options.width = parseInt(query.width);
+ }
+
return new AssetRequest({
key: decodeURI(path).replace("/files/", ""),
context: {
url: request.url
},
- options: {
- ...query,
- width: query.width ? parseInt(query.width) : undefined
- }
+ options
});
}
}
diff --git a/packages/api-file-manager/src/delivery/AssetDelivery/transformation/TransformationAssetProcessor.ts b/packages/api-file-manager/src/delivery/AssetDelivery/transformation/TransformationAssetProcessor.ts
index fbd330330a7..39d17f83d52 100644
--- a/packages/api-file-manager/src/delivery/AssetDelivery/transformation/TransformationAssetProcessor.ts
+++ b/packages/api-file-manager/src/delivery/AssetDelivery/transformation/TransformationAssetProcessor.ts
@@ -12,6 +12,7 @@ export class TransformationAssetProcessor implements AssetProcessor {
// If the `original` image was requested, we skip all transformations.
if (original) {
+ console.log("Skip transformations; original asset was requested.");
return asset;
}
diff --git a/packages/api-file-manager/src/delivery/setupAssetDelivery.ts b/packages/api-file-manager/src/delivery/setupAssetDelivery.ts
index 9a50159a9be..6e09b6394c9 100644
--- a/packages/api-file-manager/src/delivery/setupAssetDelivery.ts
+++ b/packages/api-file-manager/src/delivery/setupAssetDelivery.ts
@@ -146,6 +146,7 @@ export const setupAssetDelivery = (params: AssetDeliveryParams) => {
);
// Get reply object (runs the output strategy under the hood).
+ console.log(`Output asset (size: ${processedAsset.getSize()} bytes).`);
return outputAsset(reply, processedAsset);
},
{ override: true }
diff --git a/packages/api-headless-cms-ddb-es/src/operations/entry/elasticsearch/sort.ts b/packages/api-headless-cms-ddb-es/src/operations/entry/elasticsearch/sort.ts
index 67b5cd28260..6a9408e913a 100644
--- a/packages/api-headless-cms-ddb-es/src/operations/entry/elasticsearch/sort.ts
+++ b/packages/api-headless-cms-ddb-es/src/operations/entry/elasticsearch/sort.ts
@@ -17,7 +17,13 @@ export const createElasticsearchSort = (params: Params): esSort => {
const { sort, modelFields, plugins } = params;
if (!sort || sort.length === 0) {
- return [];
+ return [
+ {
+ ["id.keyword"]: {
+ order: "asc"
+ }
+ }
+ ];
}
const searchPlugins = createSearchPluginList({
diff --git a/packages/api-headless-cms-ddb/src/operations/entry/index.ts b/packages/api-headless-cms-ddb/src/operations/entry/index.ts
index 2ae39e96bd2..9d1c76fc93c 100644
--- a/packages/api-headless-cms-ddb/src/operations/entry/index.ts
+++ b/packages/api-headless-cms-ddb/src/operations/entry/index.ts
@@ -1020,7 +1020,7 @@ export const createEntriesStorageOperations = (
* Although we do not need a cursor here, we will use it as such to keep it standardized.
* Number is simply encoded.
*/
- const cursor = totalCount > start + limit ? encodeCursor(`${start + limit}`) : null;
+ const cursor = encodeCursor(`${start + limit}`);
return {
hasMoreItems,
totalCount,
diff --git a/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts b/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts
index 7a91f9d558b..24005cc9e5f 100644
--- a/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts
+++ b/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts
@@ -2,6 +2,7 @@ import models from "./mocks/contentModels";
import { CmsEntry, CmsGroup, CmsModel } from "~/types";
import { useCategoryManageHandler } from "../testHelpers/useCategoryManageHandler";
import { generateAlphaNumericLowerCaseId } from "@webiny/utils";
+import { createMockCmsEntry } from "~tests/helpers/createMockCmsEntry";
const manageOpts = {
path: "manage/en-US"
@@ -90,7 +91,7 @@ describe("Content Entry Meta Field", () => {
it("storage operations - should have meta field data in the retrieved record", async () => {
const { model } = await setup();
const entryId = generateAlphaNumericLowerCaseId(8);
- const entry: CmsEntry = {
+ const entry = createMockCmsEntry({
id: `${entryId}#0001`,
entryId,
version: 1,
@@ -117,7 +118,7 @@ describe("Content Entry Meta Field", () => {
status: "draft",
webinyVersion: "5.27.0",
meta: createMetaData()
- };
+ });
const createdRecord = await storageOperations.entries.create(model, {
entry,
@@ -177,7 +178,7 @@ describe("Content Entry Meta Field", () => {
meta: createMetaData()
}
],
- cursor: null,
+ cursor: expect.any(String),
totalCount: 1
});
diff --git a/packages/api-headless-cms/__tests__/helpers/createMockCmsEntry.ts b/packages/api-headless-cms/__tests__/helpers/createMockCmsEntry.ts
new file mode 100644
index 00000000000..a4288596865
--- /dev/null
+++ b/packages/api-headless-cms/__tests__/helpers/createMockCmsEntry.ts
@@ -0,0 +1,7 @@
+import { CmsEntry } from "~/types";
+
+export const createMockCmsEntry = (input: Partial): T => {
+ return {
+ ...input
+ } as T;
+};
diff --git a/packages/api-headless-cms/__tests__/storageOperations/entries.test.ts b/packages/api-headless-cms/__tests__/storageOperations/entries.test.ts
index bfab26d276f..6920c5331c5 100644
--- a/packages/api-headless-cms/__tests__/storageOperations/entries.test.ts
+++ b/packages/api-headless-cms/__tests__/storageOperations/entries.test.ts
@@ -107,7 +107,7 @@ describe("Entries storage operations", () => {
expect(result.items).toHaveLength(amount);
expect(result).toMatchObject({
- cursor: null,
+ cursor: expect.any(String),
hasMoreItems: false,
totalCount: amount
});
diff --git a/packages/api-page-builder-so-ddb-es/src/operations/pages/index.ts b/packages/api-page-builder-so-ddb-es/src/operations/pages/index.ts
index e2ce7c2ea57..2bde41b247c 100644
--- a/packages/api-page-builder-so-ddb-es/src/operations/pages/index.ts
+++ b/packages/api-page-builder-so-ddb-es/src/operations/pages/index.ts
@@ -48,6 +48,8 @@ import {
import { sortItems } from "@webiny/db-dynamodb/utils/sort";
import { PageDynamoDbElasticsearchFieldPlugin } from "~/plugins/definitions/PageDynamoDbElasticsearchFieldPlugin";
import { getClean, put } from "@webiny/db-dynamodb";
+import { shouldIgnoreEsResponseError } from "~/operations/pages/shouldIgnoreEsResponseError";
+import { logIgnoredEsResponseError } from "~/operations/pages/logIgnoredEsResponseError";
/**
* This function removes attributes that were once present in the Page record, which we no longer need.
@@ -793,7 +795,11 @@ export const createPageStorageOperations = (
* Do not throw the error if Elasticsearch index does not exist.
* In some CRUDs we try to get list of pages but index was not created yet.
*/
- if (ex.message === "index_not_found_exception") {
+ if (shouldIgnoreEsResponseError(ex)) {
+ logIgnoredEsResponseError({
+ error: ex,
+ indexName: esConfig.index
+ });
return {
items: [],
meta: {
@@ -884,6 +890,13 @@ export const createPageStorageOperations = (
}
return tags.buckets.map(item => item.key);
} catch (ex) {
+ if (shouldIgnoreEsResponseError(ex)) {
+ logIgnoredEsResponseError({
+ error: ex,
+ indexName: esConfig.index
+ });
+ return [];
+ }
throw new WebinyError(
ex.message || "Could not list tags by given parameters.",
ex.code || "LIST_TAGS_ERROR",
diff --git a/packages/api-page-builder-so-ddb-es/src/operations/pages/logIgnoredEsResponseError.ts b/packages/api-page-builder-so-ddb-es/src/operations/pages/logIgnoredEsResponseError.ts
new file mode 100644
index 00000000000..a35151ea4d6
--- /dev/null
+++ b/packages/api-page-builder-so-ddb-es/src/operations/pages/logIgnoredEsResponseError.ts
@@ -0,0 +1,23 @@
+import WebinyError from "@webiny/error";
+
+interface LogIgnoredElasticsearchExceptionParams {
+ error: WebinyError;
+ indexName: string;
+}
+
+export const logIgnoredEsResponseError = (params: LogIgnoredElasticsearchExceptionParams) => {
+ const { error, indexName } = params;
+ if (process.env.DEBUG !== "true") {
+ return;
+ }
+ console.log(`Ignoring Elasticsearch response error: ${error.message}`, {
+ usedIndexName: indexName,
+ error: {
+ ...error,
+ message: error.message,
+ code: error.code,
+ data: error.data,
+ stack: error.stack
+ }
+ });
+};
diff --git a/packages/api-page-builder-so-ddb-es/src/operations/pages/shouldIgnoreEsResponseError.ts b/packages/api-page-builder-so-ddb-es/src/operations/pages/shouldIgnoreEsResponseError.ts
new file mode 100644
index 00000000000..9455112cf02
--- /dev/null
+++ b/packages/api-page-builder-so-ddb-es/src/operations/pages/shouldIgnoreEsResponseError.ts
@@ -0,0 +1,10 @@
+import WebinyError from "@webiny/error";
+
+const IGNORED_ES_SEARCH_EXCEPTIONS = [
+ "index_not_found_exception",
+ "search_phase_execution_exception"
+];
+
+export const shouldIgnoreEsResponseError = (error: WebinyError) => {
+ return IGNORED_ES_SEARCH_EXCEPTIONS.includes(error.message);
+};
diff --git a/packages/app-headless-cms/src/admin/components/ContentEntryForm/Header/DeleteEntry/DeleteEntry.tsx b/packages/app-headless-cms/src/admin/components/ContentEntryForm/Header/DeleteEntry/DeleteEntry.tsx
index 99e6d6755b1..15c2cf3b435 100644
--- a/packages/app-headless-cms/src/admin/components/ContentEntryForm/Header/DeleteEntry/DeleteEntry.tsx
+++ b/packages/app-headless-cms/src/admin/components/ContentEntryForm/Header/DeleteEntry/DeleteEntry.tsx
@@ -27,7 +27,7 @@ export const DeleteEntry = () => {
return (
}
- label={"Delete"}
+ label={"Delete entry"}
onAction={() =>
showConfirmationDialog({
onAccept: navigateBacktoList
diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/PublishEntryRevisionListItem.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/PublishEntryRevisionListItem.tsx
index b132c47284d..1fe74526577 100644
--- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/PublishEntryRevisionListItem.tsx
+++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/PublishEntryRevisionListItem.tsx
@@ -13,7 +13,7 @@ const PublishEntryRevisionListItemComponent = () => {
} />
- {t`Publish`}
+ {t`Publish revision`}
);
};
diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionListItem.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionListItem.tsx
index 78b1fdffc3d..f7cfcd57626 100644
--- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionListItem.tsx
+++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionListItem.tsx
@@ -34,7 +34,7 @@ const t = i18n.ns("app-headless-cms/admin/plugins/content-details/content-revisi
const primaryColor = css({ color: "var(--mdc-theme-primary)" });
const revisionsMenu = css({
- width: 250,
+ width: 300,
right: -105,
left: "auto !important"
});
@@ -123,7 +123,7 @@ const RevisionListItem = ({ revision }: RevisionListItemProps) => {
} />
- {t`New from current`}
+ {t`New revision from current`}
)}
@@ -137,7 +137,7 @@ const RevisionListItem = ({ revision }: RevisionListItemProps) => {
} />
- {t`Edit`}
+ {t`Edit revision`}
)}
@@ -155,7 +155,7 @@ const RevisionListItem = ({ revision }: RevisionListItemProps) => {
} />
- {t`Unpublish`}
+ {t`Unpublish revision`}
)}
@@ -166,7 +166,7 @@ const RevisionListItem = ({ revision }: RevisionListItemProps) => {
} />
- {t` Delete`}
+ {t` Delete revision`}
)}