From 8fa8476b9200882057fcea2e8358c0bbaecaa5c5 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 14 Oct 2025 11:54:47 +0530 Subject: [PATCH 1/9] upcoming: [UIE-9311] - Add API endpoints and types for `/v4/images/sharegroups` --- packages/api-v4/src/images/images.ts | 21 +- packages/api-v4/src/images/sharedGroups.ts | 386 +++++++++++++++++++++ packages/api-v4/src/images/types.ts | 208 +++++++++++ 3 files changed, 614 insertions(+), 1 deletion(-) create mode 100644 packages/api-v4/src/images/sharedGroups.ts diff --git a/packages/api-v4/src/images/images.ts b/packages/api-v4/src/images/images.ts index b56866929a4..a0cc529e9b5 100644 --- a/packages/api-v4/src/images/images.ts +++ b/packages/api-v4/src/images/images.ts @@ -5,7 +5,7 @@ import { uploadImageSchema, } from '@linode/validation/lib/images.schema'; -import { API_ROOT } from '../constants'; +import { API_ROOT, BETA_API_ROOT } from '../constants'; import Request, { setData, setMethod, @@ -19,6 +19,7 @@ import type { CreateImagePayload, Image, ImageUploadPayload, + Sharegroup, UpdateImagePayload, UpdateImageRegionsPayload, UploadImageResponse, @@ -116,3 +117,21 @@ export const updateImageRegions = ( setData(data, updateImageRegionsSchema), ); }; + +/** + * Lists all the sharegroups a given image is present in. + * + * @param imageId { string } ID of the Image to look up. + */ +export const getSharegroupsFromImage = ( + imageId: string, + params: Params = {}, + filters: Filter = {}, +) => { + Request>( + setURL(`${BETA_API_ROOT}/images/${imageId}/sharegroups`), + setMethod('GET'), + setParams(params), + setXFilter(filters), + ); +}; diff --git a/packages/api-v4/src/images/sharedGroups.ts b/packages/api-v4/src/images/sharedGroups.ts new file mode 100644 index 00000000000..dc5cb34c60c --- /dev/null +++ b/packages/api-v4/src/images/sharedGroups.ts @@ -0,0 +1,386 @@ +import { + addSharegroupImagesSchema, + addSharegroupMemberSchema, + createSharegroupSchema, + generateSharegroupTokenSchema, + updateSharegroupImageSchema, + updateSharegroupMemberSchema, + updateSharegroupSchema, + updateSharegroupTokenSchema, +} from '@linode/validation/lib/images.schema'; + +import { BETA_API_ROOT } from '../constants'; +import Request, { + setData, + setMethod, + setParams, + setURL, + setXFilter, +} from '../request'; +import { + AddSharegroupImagesPayload, + AddSharegroupMemberPayload, + CreateSharegroupPayload, + GenerateSharegroupTokenPayload, + Image, + Sharegroup, + SharegroupMember, + SharegroupToken, + UpdateSharegroupImagePayload, + UpdateSharegroupMemberPayload, + UpdateSharegroupPayload, +} from './types'; + +import type { Filter, ResourcePage as Page, Params } from '../types'; + +/** + * Create a Image Sharegroup. + * + * @param data { createSharegroupPayload } the sharegroup details + */ +export const createSharegroup = (data: CreateSharegroupPayload) => { + return Request( + setURL(`${BETA_API_ROOT}/images/sharegroups`), + setMethod('POST'), + setData(data, createSharegroupSchema), + ); +}; + +/** + * Add Images to the Sharegroup + * + * @param sharegroupId { string } ID of the sharegroup to add images + * @param data { AddSharegroupImagesPayload } the image details + */ +export const addImagesToSharegroup = ( + sharegroupId: number, + data: AddSharegroupImagesPayload, +) => { + return Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/images`, + ), + setMethod('POST'), + setData(data, addSharegroupImagesSchema), + ); +}; + +/** + * Add Member to the Sharegroup + * + * @param sharegroupId {string} ID of the Sharegroup to add member + * @param data {AddSharegroupMemberPayload} the Member details + */ +export const addMembersToSharegroup = ( + sharegroupId: number, + data: AddSharegroupMemberPayload, +) => { + return Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/images`, + ), + setMethod('POST'), + setData(data, addSharegroupMemberSchema), + ); +}; + +/** + * Generate user token for the Sharegroup + * + * @param data {GenerateSharegroupTokenPayload} the token details + */ +export const generateSharegroupToken = ( + data: GenerateSharegroupTokenPayload, +) => { + return Request( + setURL(`${BETA_API_ROOT}/images/sharegroup/tokens`), + setMethod('POST'), + setData(data, generateSharegroupTokenSchema), + ); +}; + +/** + * Returns a paginated list of Sharegroups + */ +export const getSharegroups = (params: Params = {}, filters: Filter = {}) => + Request>( + setURL(`${BETA_API_ROOT}/images/sharegroups`), + setMethod('GET'), + setParams(params), + setXFilter(filters), + ); + +/** + * Get information about a single Sharegroup + * + * @param sharegroupId {string} ID of the Sharegroup to look up + */ +export const getSharegroup = (sharegroupId: string) => + Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}`, + ), + setMethod('GET'), + ); + +/** + * Get details of the Sharegroup the token has been accepted into + * + * @param token_uuid {string} Token UUID of the user + */ +export const getSharegroupFromToken = (token_uuid: string) => { + Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/tokens/${encodeURIComponent(token_uuid)}/sharegroup`, + ), + setMethod('GET'), + ); +}; + +/** + * Get a paginated list of Images present in a Sharegroup + * + * @param sharegroupId {string} ID of the Sharegroup to look up + */ +export const getSharegroupImages = ( + sharegroupId: string, + params: Params = {}, + filters: Filter = {}, +) => + Request>( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/images`, + ), + setMethod('GET'), + setParams(params), + setXFilter(filters), + ); + +/** + * Get a paginated list of Sharegroup Images the token has been accepted into + * + * @param token_uuid {string} Token UUID of the user + */ +export const getSharegroupImagesFromToken = ( + token_uuid: string, + params: Params = {}, + filters: Filter = {}, +) => { + Request>( + setURL( + `${BETA_API_ROOT}/images/sharegroups/tokens/${encodeURIComponent(token_uuid)}/sharegroup/images`, + ), + setMethod('GET'), + setParams(params), + setXFilter(filters), + ); +}; + +/** + * Get a paginated list of members part of the Sharegroup + * + * @param sharegroupId {string} ID of the Sharegroup to look up + */ +export const getSharegroupMembers = ( + sharegroupId: string, + params: Params = {}, + filters: Filter = {}, +) => { + Request>( + setURL( + `${BETA_API_ROOT}/images/sharegroup/${encodeURIComponent(sharegroupId)}/members`, + ), + setMethod('GET'), + setParams(params), + setXFilter(filters), + ); +}; + +/** + * Get member details of a user from the Sharegroup + * + * @param sharegroupId {string} ID of the Sharegroup to look up + * @param token_uuid {string} Token UUID of the user to look up + */ +export const getSharegroupMember = ( + sharegroupId: string, + token_uuid: string, +) => { + Request( + setURL( + `${BETA_API_ROOT}/images/sharegroup/${encodeURIComponent(sharegroupId)}/members/${encodeURIComponent(token_uuid)}`, + ), + setMethod('GET'), + ); +}; + +/** + * Returns a paginated list of tokens created by the user + */ +export const getUserSharegroupTokens = ( + params: Params = {}, + filters: Filter = {}, +) => { + Request>( + setURL(`${BETA_API_ROOT}/images/sharegroups/tokens`), + setMethod('GET'), + setParams(params), + setXFilter(filters), + ); +}; + +/** + * Get details about a specific token created by the user + * + * @param token_uuid Token UUID of the user to look up + */ +export const getUserSharegroupToken = (token_uuid: string) => { + Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/tokens/${encodeURIComponent(token_uuid)}`, + ), + setMethod('GET'), + ); +}; + +/** + * Update a Sharegroup. + * + * @param sharegroupId {string} ID of the Sharegroup to update + * @param data { updateSharegroupPayload } the sharegroup details + */ +export const updateSharegroup = ( + sharegroupId: string, + data: UpdateSharegroupPayload, +) => { + return Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}`, + ), + setMethod('PUT'), + setData(data, updateSharegroupSchema), + ); +}; + +/** + * Update a Image in a Sharegroup. + * + * @param sharegroupId {string} ID of the Sharegroup the image belongs to + * @param imageId {string} ID of the Image to update + * @param data { UpdateSharegroupImagePayload } the updated image details + */ +export const updateSharegroupImage = ( + sharegroupId: string, + imageId: string, + data: UpdateSharegroupImagePayload, +) => { + return Request( + setURL( + `${BETA_API_ROOT}/images/sharegroup/${encodeURIComponent(sharegroupId)}/images/${encodeURIComponent(imageId)}}`, + ), + setMethod('PUT'), + setData(data, updateSharegroupImageSchema), + ); +}; + +/** + * Update a user token's label + * + * @param token_uuid {string} token UUID of the user + * @param data {UpdateSharegroupMemberPayload} the updated label + */ +export const updateSharegroupToken = ( + token_uuid: string, + data: UpdateSharegroupMemberPayload, +) => { + return Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/tokens/${encodeURIComponent(token_uuid)}`, + ), + setMethod('PUT'), + setData(data, updateSharegroupTokenSchema), + ); +}; + +/** + * Update a Sharegroup member's label + * + * @param token_uuid {string} token UUID of the user + * @param data {UpdateSharegroupMemberPayload} the updated label + */ +export const updateSharegroupMember = ( + sharegroupId: string, + token_uuid: string, + data: UpdateSharegroupMemberPayload, +) => { + return Request( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/members/${encodeURIComponent(token_uuid)}`, + ), + setMethod('PUT'), + setData(data, updateSharegroupMemberSchema), + ); +}; + +/** + * Delete a sharegroup + * + * @param sharegroupId {string} ID of the sharegroup to delete + */ +export const deleteSharegroup = (sharegroupId: string) => { + return Request<{}>( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}`, + ), + setMethod('DELETE'), + ); +}; + +/** + * Delete a sharegroup Image + * + * @param sharegroupId {string} ID of the sharegroup to delete + * @param imageId {string} ID of the image to delete + */ +export const deleteSharegroupImage = ( + sharegroupId: string, + imageId: string, +) => { + return Request<{}>( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/images/${encodeURIComponent(imageId)}`, + ), + setMethod('DELETE'), + ); +}; + +/** + * Delete a user token + * + * @param token_uuid {string} Token UUID of the user to delete + */ +export const deleteSharegroupToken = (token_uuid: string) => { + return Request<{}>( + setURL( + `${BETA_API_ROOT}/images/sharegroups/tokens/${encodeURIComponent(token_uuid)}`, + ), + setMethod('DELETE'), + ); +}; + +/** + * Delete a sharegroup Member + * + * @param token_uuid {string} Token UUID of the member to delete + */ +export const deleteSharegroupMember = ( + sharegroupId: string, + token_uuid: string, +) => { + return Request<{}>( + setURL( + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/members/${encodeURIComponent(token_uuid)}`, + ), + setMethod('DELETE'), + ); +}; diff --git a/packages/api-v4/src/images/types.ts b/packages/api-v4/src/images/types.ts index 8eb4c68fbe9..8fa425f7524 100644 --- a/packages/api-v4/src/images/types.ts +++ b/packages/api-v4/src/images/types.ts @@ -4,6 +4,8 @@ export type ImageCapabilities = 'cloud-init' | 'distributed-sites'; type ImageType = 'automatic' | 'manual'; +type SharegroupMemberStatus = 'active' | 'revoked'; + export type ImageRegionStatus = | 'available' | 'creating' @@ -18,6 +20,19 @@ export interface ImageRegion { status: ImageRegionStatus; } +export interface ImageSharingData { + shared_by: null | { + sharegroup_id: number; + sharegroup_label: string; + sharegroup_uuid: string; + source_image_id: number; + }; + shared_with: null | { + shared_count: number; + sharegroup_list_url: string; + }; +} + export interface Image { /** * A list of the capabilities of this image. @@ -59,11 +74,21 @@ export interface Image { */ id: string; + /** + * Image sharing attributes for private and shared images. + */ + image_sharing?: ImageSharingData; + /** * Whether this image is marked for public distribution. */ is_public: boolean; + /** + * Whether this image has a shared copy. + */ + is_shared?: boolean; + /** * A short description of this image. */ @@ -157,3 +182,186 @@ export interface UpdateImageRegionsPayload { */ regions: string[]; } + +export interface SharegroupImagePayload { + /** + * A detailed description of this Image. + */ + description?: string; + /** + * ID of the private image that will be added to the Sharegroup + */ + id: string; + /** + * A short title of this Image. + * + * Defaults to the label of the private image it is being created from if not provided. + */ + label?: string; +} + +export interface CreateSharegroupPayload { + /** + * A detailed description of this Sharegroup. + */ + description?: string; + /** + * An array of images that will be shared in the Sharegroup + */ + images?: SharegroupImagePayload[]; + /** + * A short title of this Sharegroup. + */ + label: string; +} + +export type UpdateSharegroupPayload = Omit; + +export interface AddSharegroupImagesPayload { + /** + * An array of images that will be shared in the Sharegroup + */ + images: SharegroupImagePayload[]; +} + +export type UpdateSharegroupImagePayload = Omit; + +export interface Sharegroup { + /** + * The timestamp of when the Sharegroup was created + */ + created: string; + /** + * A detailed description for the Sharegroup + */ + description: string; + /** + * The timestamp of when the Sharegroup would expire + */ + expiry?: string; + /** + * The ID of the this Sharegroup. + */ + id: number; + /** + * The number of images shared in the Sharegroup + */ + images_count?: number; + /** + * A boolean that indicates if the Sharegroup is suspended + */ + is_suspended: boolean; + /** + * A short title for the Sharegroup + */ + label: string; + /** + * The number of members present in the Sharegroup + */ + members_count?: number; + /** + * The timestamp of when the Sharegroup was last updated + */ + updated: string; + /** + * A unique identifier for the sharegroup which can be used to generate member tokens + */ + uuid: string; +} + +export interface AddSharegroupMemberPayload { + /** + * The title given to the user in the sharegroup + */ + label: string; + /** + * The user token shared by the user to join the sharegroup + */ + token: string; +} + +export type UpdateSharegroupMemberPayload = Omit< + AddSharegroupMemberPayload, + 'token' +>; + +export interface SharegroupMember { + /** + * The timestamp of when the member was added to the sharegroup + */ + created: string; + /** + * The timestamp of when the member's token expires + */ + expiry: string; + /** + * The title given to the user in the sharegroup + */ + label: string; + /** + * The status of the member in the current sharegroup + */ + status: SharegroupMemberStatus; + /** + * A unique identifier for member tokens + */ + token_uuid: string; + /** + * The timestamp of when the member's information was last updated + */ + updated: string; +} + +export interface GenerateSharegroupTokenPayload { + /** + * The title given to the user in the sharegroup + */ + label?: string; + /** + * The sharegroup UUID for which a user token will be generated + */ + valid_for_sharegroup_uuid: string; +} + +export interface SharegroupToken { + /** + * The timestamp of when the token was created + */ + created: string; + /** + * The timestamp of when the token will expire + */ + expiry: string; + /** + * The title given to the user in the sharegroup + */ + label: string; + /** + * The sharegroup label this token is created for + */ + sharegroup_label: string; + /** + * The sharegroup UUID the token is created for + */ + sharegroup_uuid: string; + /** + * The current status of this token + */ + status: string; + /** + * A unique member token to join the sharegroup + */ + token: string; + /** + * A unique identifier for each token generated + */ + token_uuid: string; + /** + * The timestamp of when the token was last updated + */ + updated: string; + /** + * The sharegroup UUID the token is valid for + */ + valid_for_sharegroup_uuid: string; +} From e2fdce20a303385709f168d7e9a19e5810a2fc11 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 14 Oct 2025 12:09:32 +0530 Subject: [PATCH 2/9] Add validation schema --- packages/validation/src/images.schema.ts | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/validation/src/images.schema.ts b/packages/validation/src/images.schema.ts index 9ac35a1c027..fc9e63b1ba1 100644 --- a/packages/validation/src/images.schema.ts +++ b/packages/validation/src/images.schema.ts @@ -39,3 +39,48 @@ export const updateImageRegionsSchema = object({ .required('Regions are required.') .min(1, 'Must specify at least one region.'), }); + +export const sharegroupImageSchema = object({ + id: string().required('Image ID is required'), + label: labelSchema.optional(), + description: string().optional().max(65000), +}); + +export const addSharegroupImagesSchema = object({ + images: array(sharegroupImageSchema).required('Images are required.'), +}); + +export const createSharegroupSchema = object({ + label: labelSchema.required('Label is required.'), + description: string().optional().max(65000), + images: array(sharegroupImageSchema).notRequired(), +}); + +export const addSharegroupMemberSchema = object({ + token: string().required('Token is required.'), + label: labelSchema.required('Label is required.'), +}); + +export const generateSharegroupTokenSchema = object({ + label: labelSchema.optional(), + valid_for_sharegroup_uuid: boolean().required( + 'Valid sharegroup UUID required.', + ), +}); + +export const updateSharegroupSchema = object({ + label: labelSchema.optional(), + description: string() + .optional() + .max(65000, 'Length must be 65000 characters or less.'), +}); + +export const updateSharegroupImageSchema = updateSharegroupSchema.shape({}); + +export const updateSharegroupTokenSchema = object({ + label: labelSchema.required('Label is required.'), +}); + +export const updateSharegroupMemberSchema = updateSharegroupTokenSchema.shape( + {}, +); From e96cc59948f77fe9a24eb8d28cd7f4975f4614f4 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 14 Oct 2025 13:38:40 +0530 Subject: [PATCH 3/9] upcoming: [UIE-9343] - Add API endpoints and types for `/v4beta/images/sharegroups/members` and `/v4beta/images/sharegroups/tokens` --- packages/api-v4/src/images/sharedGroups.ts | 161 +-------------------- packages/api-v4/src/images/types.ts | 43 ------ packages/validation/src/images.schema.ts | 25 ---- 3 files changed, 3 insertions(+), 226 deletions(-) diff --git a/packages/api-v4/src/images/sharedGroups.ts b/packages/api-v4/src/images/sharedGroups.ts index dc5cb34c60c..ceed8fafb4b 100644 --- a/packages/api-v4/src/images/sharedGroups.ts +++ b/packages/api-v4/src/images/sharedGroups.ts @@ -1,11 +1,7 @@ import { - addSharegroupImagesSchema, addSharegroupMemberSchema, - createSharegroupSchema, generateSharegroupTokenSchema, - updateSharegroupImageSchema, updateSharegroupMemberSchema, - updateSharegroupSchema, updateSharegroupTokenSchema, } from '@linode/validation/lib/images.schema'; @@ -17,54 +13,18 @@ import Request, { setURL, setXFilter, } from '../request'; -import { - AddSharegroupImagesPayload, + +import type { Filter, ResourcePage as Page, Params } from '../types'; +import type { AddSharegroupMemberPayload, - CreateSharegroupPayload, GenerateSharegroupTokenPayload, Image, Sharegroup, SharegroupMember, SharegroupToken, - UpdateSharegroupImagePayload, UpdateSharegroupMemberPayload, - UpdateSharegroupPayload, } from './types'; -import type { Filter, ResourcePage as Page, Params } from '../types'; - -/** - * Create a Image Sharegroup. - * - * @param data { createSharegroupPayload } the sharegroup details - */ -export const createSharegroup = (data: CreateSharegroupPayload) => { - return Request( - setURL(`${BETA_API_ROOT}/images/sharegroups`), - setMethod('POST'), - setData(data, createSharegroupSchema), - ); -}; - -/** - * Add Images to the Sharegroup - * - * @param sharegroupId { string } ID of the sharegroup to add images - * @param data { AddSharegroupImagesPayload } the image details - */ -export const addImagesToSharegroup = ( - sharegroupId: number, - data: AddSharegroupImagesPayload, -) => { - return Request( - setURL( - `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/images`, - ), - setMethod('POST'), - setData(data, addSharegroupImagesSchema), - ); -}; - /** * Add Member to the Sharegroup * @@ -99,30 +59,6 @@ export const generateSharegroupToken = ( ); }; -/** - * Returns a paginated list of Sharegroups - */ -export const getSharegroups = (params: Params = {}, filters: Filter = {}) => - Request>( - setURL(`${BETA_API_ROOT}/images/sharegroups`), - setMethod('GET'), - setParams(params), - setXFilter(filters), - ); - -/** - * Get information about a single Sharegroup - * - * @param sharegroupId {string} ID of the Sharegroup to look up - */ -export const getSharegroup = (sharegroupId: string) => - Request( - setURL( - `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}`, - ), - setMethod('GET'), - ); - /** * Get details of the Sharegroup the token has been accepted into * @@ -137,25 +73,6 @@ export const getSharegroupFromToken = (token_uuid: string) => { ); }; -/** - * Get a paginated list of Images present in a Sharegroup - * - * @param sharegroupId {string} ID of the Sharegroup to look up - */ -export const getSharegroupImages = ( - sharegroupId: string, - params: Params = {}, - filters: Filter = {}, -) => - Request>( - setURL( - `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/images`, - ), - setMethod('GET'), - setParams(params), - setXFilter(filters), - ); - /** * Get a paginated list of Sharegroup Images the token has been accepted into * @@ -243,46 +160,6 @@ export const getUserSharegroupToken = (token_uuid: string) => { ); }; -/** - * Update a Sharegroup. - * - * @param sharegroupId {string} ID of the Sharegroup to update - * @param data { updateSharegroupPayload } the sharegroup details - */ -export const updateSharegroup = ( - sharegroupId: string, - data: UpdateSharegroupPayload, -) => { - return Request( - setURL( - `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}`, - ), - setMethod('PUT'), - setData(data, updateSharegroupSchema), - ); -}; - -/** - * Update a Image in a Sharegroup. - * - * @param sharegroupId {string} ID of the Sharegroup the image belongs to - * @param imageId {string} ID of the Image to update - * @param data { UpdateSharegroupImagePayload } the updated image details - */ -export const updateSharegroupImage = ( - sharegroupId: string, - imageId: string, - data: UpdateSharegroupImagePayload, -) => { - return Request( - setURL( - `${BETA_API_ROOT}/images/sharegroup/${encodeURIComponent(sharegroupId)}/images/${encodeURIComponent(imageId)}}`, - ), - setMethod('PUT'), - setData(data, updateSharegroupImageSchema), - ); -}; - /** * Update a user token's label * @@ -322,38 +199,6 @@ export const updateSharegroupMember = ( ); }; -/** - * Delete a sharegroup - * - * @param sharegroupId {string} ID of the sharegroup to delete - */ -export const deleteSharegroup = (sharegroupId: string) => { - return Request<{}>( - setURL( - `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}`, - ), - setMethod('DELETE'), - ); -}; - -/** - * Delete a sharegroup Image - * - * @param sharegroupId {string} ID of the sharegroup to delete - * @param imageId {string} ID of the image to delete - */ -export const deleteSharegroupImage = ( - sharegroupId: string, - imageId: string, -) => { - return Request<{}>( - setURL( - `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/images/${encodeURIComponent(imageId)}`, - ), - setMethod('DELETE'), - ); -}; - /** * Delete a user token * diff --git a/packages/api-v4/src/images/types.ts b/packages/api-v4/src/images/types.ts index 8fa425f7524..19f10d7fb79 100644 --- a/packages/api-v4/src/images/types.ts +++ b/packages/api-v4/src/images/types.ts @@ -183,49 +183,6 @@ export interface UpdateImageRegionsPayload { regions: string[]; } -export interface SharegroupImagePayload { - /** - * A detailed description of this Image. - */ - description?: string; - /** - * ID of the private image that will be added to the Sharegroup - */ - id: string; - /** - * A short title of this Image. - * - * Defaults to the label of the private image it is being created from if not provided. - */ - label?: string; -} - -export interface CreateSharegroupPayload { - /** - * A detailed description of this Sharegroup. - */ - description?: string; - /** - * An array of images that will be shared in the Sharegroup - */ - images?: SharegroupImagePayload[]; - /** - * A short title of this Sharegroup. - */ - label: string; -} - -export type UpdateSharegroupPayload = Omit; - -export interface AddSharegroupImagesPayload { - /** - * An array of images that will be shared in the Sharegroup - */ - images: SharegroupImagePayload[]; -} - -export type UpdateSharegroupImagePayload = Omit; - export interface Sharegroup { /** * The timestamp of when the Sharegroup was created diff --git a/packages/validation/src/images.schema.ts b/packages/validation/src/images.schema.ts index fc9e63b1ba1..19006882919 100644 --- a/packages/validation/src/images.schema.ts +++ b/packages/validation/src/images.schema.ts @@ -40,22 +40,6 @@ export const updateImageRegionsSchema = object({ .min(1, 'Must specify at least one region.'), }); -export const sharegroupImageSchema = object({ - id: string().required('Image ID is required'), - label: labelSchema.optional(), - description: string().optional().max(65000), -}); - -export const addSharegroupImagesSchema = object({ - images: array(sharegroupImageSchema).required('Images are required.'), -}); - -export const createSharegroupSchema = object({ - label: labelSchema.required('Label is required.'), - description: string().optional().max(65000), - images: array(sharegroupImageSchema).notRequired(), -}); - export const addSharegroupMemberSchema = object({ token: string().required('Token is required.'), label: labelSchema.required('Label is required.'), @@ -68,15 +52,6 @@ export const generateSharegroupTokenSchema = object({ ), }); -export const updateSharegroupSchema = object({ - label: labelSchema.optional(), - description: string() - .optional() - .max(65000, 'Length must be 65000 characters or less.'), -}); - -export const updateSharegroupImageSchema = updateSharegroupSchema.shape({}); - export const updateSharegroupTokenSchema = object({ label: labelSchema.required('Label is required.'), }); From c12d9ab8958f5212876daacb58bc3f50a0fa3b5b Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 14 Oct 2025 14:22:45 +0530 Subject: [PATCH 4/9] rename sharegroup.ts --- packages/api-v4/src/images/{sharedGroups.ts => sharegroup.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/api-v4/src/images/{sharedGroups.ts => sharegroup.ts} (100%) diff --git a/packages/api-v4/src/images/sharedGroups.ts b/packages/api-v4/src/images/sharegroup.ts similarity index 100% rename from packages/api-v4/src/images/sharedGroups.ts rename to packages/api-v4/src/images/sharegroup.ts From ef8a7da0adc9284587f869b565cac61c697de565 Mon Sep 17 00:00:00 2001 From: hrao Date: Tue, 14 Oct 2025 19:43:36 +0530 Subject: [PATCH 5/9] add named arguments for functions with more than 2 parameters --- packages/api-v4/src/images/sharegroup.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/api-v4/src/images/sharegroup.ts b/packages/api-v4/src/images/sharegroup.ts index ceed8fafb4b..28499f05023 100644 --- a/packages/api-v4/src/images/sharegroup.ts +++ b/packages/api-v4/src/images/sharegroup.ts @@ -185,11 +185,17 @@ export const updateSharegroupToken = ( * @param token_uuid {string} token UUID of the user * @param data {UpdateSharegroupMemberPayload} the updated label */ -export const updateSharegroupMember = ( - sharegroupId: string, - token_uuid: string, - data: UpdateSharegroupMemberPayload, -) => { +interface UpdateSharegroupMember { + data: UpdateSharegroupMemberPayload; + sharegroupId: string; + token_uuid: string; +} + +export const updateSharegroupMember = ({ + sharegroupId, + token_uuid, + data, +}: UpdateSharegroupMember) => { return Request( setURL( `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/members/${encodeURIComponent(token_uuid)}`, From 4cfe29df903b173b8d79fca4f46f192180edfe32 Mon Sep 17 00:00:00 2001 From: hrao Date: Wed, 15 Oct 2025 16:00:30 +0530 Subject: [PATCH 6/9] pr split + validation schema separation --- packages/api-v4/src/images/images.ts | 21 +-------------------- packages/validation/src/images.schema.ts | 6 +++--- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/packages/api-v4/src/images/images.ts b/packages/api-v4/src/images/images.ts index a0cc529e9b5..b56866929a4 100644 --- a/packages/api-v4/src/images/images.ts +++ b/packages/api-v4/src/images/images.ts @@ -5,7 +5,7 @@ import { uploadImageSchema, } from '@linode/validation/lib/images.schema'; -import { API_ROOT, BETA_API_ROOT } from '../constants'; +import { API_ROOT } from '../constants'; import Request, { setData, setMethod, @@ -19,7 +19,6 @@ import type { CreateImagePayload, Image, ImageUploadPayload, - Sharegroup, UpdateImagePayload, UpdateImageRegionsPayload, UploadImageResponse, @@ -117,21 +116,3 @@ export const updateImageRegions = ( setData(data, updateImageRegionsSchema), ); }; - -/** - * Lists all the sharegroups a given image is present in. - * - * @param imageId { string } ID of the Image to look up. - */ -export const getSharegroupsFromImage = ( - imageId: string, - params: Params = {}, - filters: Filter = {}, -) => { - Request>( - setURL(`${BETA_API_ROOT}/images/${imageId}/sharegroups`), - setMethod('GET'), - setParams(params), - setXFilter(filters), - ); -}; diff --git a/packages/validation/src/images.schema.ts b/packages/validation/src/images.schema.ts index 19006882919..e5e84bae480 100644 --- a/packages/validation/src/images.schema.ts +++ b/packages/validation/src/images.schema.ts @@ -56,6 +56,6 @@ export const updateSharegroupTokenSchema = object({ label: labelSchema.required('Label is required.'), }); -export const updateSharegroupMemberSchema = updateSharegroupTokenSchema.shape( - {}, -); +export const updateSharegroupMemberSchema = object({ + label: labelSchema.required('Label is required.'), +}); From cd173b6dc1a2ae9a55aff93f56be952e1d1a1090 Mon Sep 17 00:00:00 2001 From: hrao Date: Thu, 16 Oct 2025 16:45:21 +0530 Subject: [PATCH 7/9] PR feedback @pmakode-akamai --- packages/api-v4/src/images/sharegroup.ts | 8 ++++---- packages/api-v4/src/images/types.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/api-v4/src/images/sharegroup.ts b/packages/api-v4/src/images/sharegroup.ts index 28499f05023..811ef234bd5 100644 --- a/packages/api-v4/src/images/sharegroup.ts +++ b/packages/api-v4/src/images/sharegroup.ts @@ -53,7 +53,7 @@ export const generateSharegroupToken = ( data: GenerateSharegroupTokenPayload, ) => { return Request( - setURL(`${BETA_API_ROOT}/images/sharegroup/tokens`), + setURL(`${BETA_API_ROOT}/images/sharegroups/tokens`), setMethod('POST'), setData(data, generateSharegroupTokenSchema), ); @@ -85,7 +85,7 @@ export const getSharegroupImagesFromToken = ( ) => { Request>( setURL( - `${BETA_API_ROOT}/images/sharegroups/tokens/${encodeURIComponent(token_uuid)}/sharegroup/images`, + `${BETA_API_ROOT}/images/sharegroups/tokens/${encodeURIComponent(token_uuid)}/sharegroups/images`, ), setMethod('GET'), setParams(params), @@ -105,7 +105,7 @@ export const getSharegroupMembers = ( ) => { Request>( setURL( - `${BETA_API_ROOT}/images/sharegroup/${encodeURIComponent(sharegroupId)}/members`, + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/members`, ), setMethod('GET'), setParams(params), @@ -125,7 +125,7 @@ export const getSharegroupMember = ( ) => { Request( setURL( - `${BETA_API_ROOT}/images/sharegroup/${encodeURIComponent(sharegroupId)}/members/${encodeURIComponent(token_uuid)}`, + `${BETA_API_ROOT}/images/sharegroups/${encodeURIComponent(sharegroupId)}/members/${encodeURIComponent(token_uuid)}`, ), setMethod('GET'), ); diff --git a/packages/api-v4/src/images/types.ts b/packages/api-v4/src/images/types.ts index 19f10d7fb79..f77301675e7 100644 --- a/packages/api-v4/src/images/types.ts +++ b/packages/api-v4/src/images/types.ts @@ -28,7 +28,7 @@ export interface ImageSharingData { source_image_id: number; }; shared_with: null | { - shared_count: number; + sharegroup_count: number; sharegroup_list_url: string; }; } From c317f6efe8561d3793365b9eef2c11fbb6480a5e Mon Sep 17 00:00:00 2001 From: hrao Date: Fri, 24 Oct 2025 17:55:32 +0530 Subject: [PATCH 8/9] Added changeset: Add endpoints for `/v4/images/sharegroups/members` and `/v4/images/sharegroups/tokens` --- .../.changeset/pr-12984-upcoming-features-1761308732683.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/api-v4/.changeset/pr-12984-upcoming-features-1761308732683.md diff --git a/packages/api-v4/.changeset/pr-12984-upcoming-features-1761308732683.md b/packages/api-v4/.changeset/pr-12984-upcoming-features-1761308732683.md new file mode 100644 index 00000000000..588b2a0b406 --- /dev/null +++ b/packages/api-v4/.changeset/pr-12984-upcoming-features-1761308732683.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Upcoming Features +--- + +Add endpoints for `/v4/images/sharegroups/members` and `/v4/images/sharegroups/tokens` ([#12984](https://github.com/linode/manager/pull/12984)) From 07522b77875942b844ccf44477e857df3e1df1ed Mon Sep 17 00:00:00 2001 From: hrao Date: Fri, 24 Oct 2025 17:56:58 +0530 Subject: [PATCH 9/9] Added changeset: Add validation schemas for creating and updating Sharegroup Members and Tokens --- .../.changeset/pr-12984-upcoming-features-1761308818735.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/validation/.changeset/pr-12984-upcoming-features-1761308818735.md diff --git a/packages/validation/.changeset/pr-12984-upcoming-features-1761308818735.md b/packages/validation/.changeset/pr-12984-upcoming-features-1761308818735.md new file mode 100644 index 00000000000..98a1993c429 --- /dev/null +++ b/packages/validation/.changeset/pr-12984-upcoming-features-1761308818735.md @@ -0,0 +1,5 @@ +--- +"@linode/validation": Upcoming Features +--- + +Add validation schemas for creating and updating Sharegroup Members and Tokens ([#12984](https://github.com/linode/manager/pull/12984))