@@ -95,6 +95,7 @@ describe("r2", () => {
9595 wrangler r2 bucket domain Manage custom domains for an R2 bucket
9696 wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket
9797 wrangler r2 bucket lifecycle Manage lifecycle rules for an R2 bucket
98+ wrangler r2 bucket cors Manage CORS configuration for an R2 bucket
9899
99100 GLOBAL FLAGS
100101 -c, --config Path to Wrangler configuration file [string]
@@ -132,6 +133,7 @@ describe("r2", () => {
132133 wrangler r2 bucket domain Manage custom domains for an R2 bucket
133134 wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket
134135 wrangler r2 bucket lifecycle Manage lifecycle rules for an R2 bucket
136+ wrangler r2 bucket cors Manage CORS configuration for an R2 bucket
135137
136138 GLOBAL FLAGS
137139 -c, --config Path to Wrangler configuration file [string]
@@ -2055,6 +2057,155 @@ describe("r2", () => {
20552057 } ) ;
20562058 } ) ;
20572059 } ) ;
2060+ describe ( "cors" , ( ) => {
2061+ const { setIsTTY } = useMockIsTTY ( ) ;
2062+ mockAccountId ( ) ;
2063+ mockApiToken ( ) ;
2064+ describe ( "list" , ( ) => {
2065+ it ( "should list CORS rules when they exist" , async ( ) => {
2066+ const bucketName = "my-bucket" ;
2067+ const corsRules = [
2068+ {
2069+ allowed : {
2070+ origins : [ "https://www.example.com" ] ,
2071+ methods : [ "GET" , "PUT" ] ,
2072+ headers : [ "Content-Type" , "Authorization" ] ,
2073+ } ,
2074+ exposeHeaders : [ "ETag" , "Content-Length" ] ,
2075+ maxAgeSeconds : 8640 ,
2076+ } ,
2077+ ] ;
2078+
2079+ msw . use (
2080+ http . get (
2081+ "*/accounts/:accountId/r2/buckets/:bucketName/cors" ,
2082+ async ( { params } ) => {
2083+ const { accountId, bucketName : bucketParam } = params ;
2084+ expect ( accountId ) . toEqual ( "some-account-id" ) ;
2085+ expect ( bucketParam ) . toEqual ( bucketName ) ;
2086+ return HttpResponse . json (
2087+ createFetchResult ( {
2088+ rules : corsRules ,
2089+ } )
2090+ ) ;
2091+ } ,
2092+ { once : true }
2093+ )
2094+ ) ;
2095+ await runWrangler ( `r2 bucket cors list ${ bucketName } ` ) ;
2096+ expect ( std . out ) . toMatchInlineSnapshot ( `
2097+ "Listing CORS rules for bucket 'my-bucket'...
2098+ allowed_origins: https://www.example.com
2099+ allowed_methods: GET, PUT
2100+ allowed_headers: Content-Type, Authorization
2101+ exposed_headers: ETag, Content-Length
2102+ max_age_seconds: 8640"
2103+ ` ) ;
2104+ } ) ;
2105+ } ) ;
2106+ describe ( "set" , ( ) => {
2107+ it ( "should set CORS configuration from a JSON file" , async ( ) => {
2108+ const bucketName = "my-bucket" ;
2109+ const filePath = "cors-configuration.json" ;
2110+ const corsRules = {
2111+ rules : [
2112+ {
2113+ allowed : {
2114+ origins : [ "https://www.example.com" ] ,
2115+ methods : [ "GET" , "PUT" ] ,
2116+ headers : [ "Content-Type" , "Authorization" ] ,
2117+ } ,
2118+ exposeHeaders : [ "ETag" , "Content-Length" ] ,
2119+ maxAgeSeconds : 8640 ,
2120+ } ,
2121+ ] ,
2122+ } ;
2123+
2124+ writeFileSync ( filePath , JSON . stringify ( corsRules ) ) ;
2125+
2126+ setIsTTY ( true ) ;
2127+ mockConfirm ( {
2128+ text : `Are you sure you want to overwrite the existing CORS configuration for bucket '${ bucketName } '?` ,
2129+ result : true ,
2130+ } ) ;
2131+
2132+ msw . use (
2133+ http . put (
2134+ "*/accounts/:accountId/r2/buckets/:bucketName/cors" ,
2135+ async ( { request, params } ) => {
2136+ const { accountId, bucketName : bucketParam } = params ;
2137+ expect ( accountId ) . toEqual ( "some-account-id" ) ;
2138+ expect ( bucketName ) . toEqual ( bucketParam ) ;
2139+ const requestBody = await request . json ( ) ;
2140+ expect ( requestBody ) . toEqual ( {
2141+ ...corsRules ,
2142+ } ) ;
2143+ return HttpResponse . json ( createFetchResult ( { } ) ) ;
2144+ } ,
2145+ { once : true }
2146+ )
2147+ ) ;
2148+
2149+ await runWrangler (
2150+ `r2 bucket cors set ${ bucketName } --file ${ filePath } `
2151+ ) ;
2152+ expect ( std . out ) . toMatchInlineSnapshot ( `
2153+ "Setting CORS configuration (1 rules) for bucket 'my-bucket'...
2154+ ✨ Set CORS configuration for bucket 'my-bucket'."
2155+ ` ) ;
2156+ } ) ;
2157+ } ) ;
2158+ describe ( "delete" , ( ) => {
2159+ it ( "should delete CORS configuration as expected" , async ( ) => {
2160+ const bucketName = "my-bucket" ;
2161+ const corsRules = {
2162+ rules : [
2163+ {
2164+ allowed : {
2165+ origins : [ "https://www.example.com" ] ,
2166+ methods : [ "GET" , "PUT" ] ,
2167+ headers : [ "Content-Type" , "Authorization" ] ,
2168+ } ,
2169+ exposeHeaders : [ "ETag" , "Content-Length" ] ,
2170+ maxAgeSeconds : 8640 ,
2171+ } ,
2172+ ] ,
2173+ } ;
2174+ setIsTTY ( true ) ;
2175+ mockConfirm ( {
2176+ text : `Are you sure you want to clear the existing CORS configuration for bucket '${ bucketName } '?` ,
2177+ result : true ,
2178+ } ) ;
2179+ msw . use (
2180+ http . get (
2181+ "*/accounts/:accountId/r2/buckets/:bucketName/cors" ,
2182+ async ( { params } ) => {
2183+ const { accountId, bucketName : bucketParam } = params ;
2184+ expect ( accountId ) . toEqual ( "some-account-id" ) ;
2185+ expect ( bucketParam ) . toEqual ( bucketName ) ;
2186+ return HttpResponse . json ( createFetchResult ( corsRules ) ) ;
2187+ } ,
2188+ { once : true }
2189+ ) ,
2190+ http . delete (
2191+ "*/accounts/:accountId/r2/buckets/:bucketName/cors" ,
2192+ async ( { params } ) => {
2193+ const { accountId, bucketName : bucketParam } = params ;
2194+ expect ( accountId ) . toEqual ( "some-account-id" ) ;
2195+ expect ( bucketName ) . toEqual ( bucketParam ) ;
2196+ return HttpResponse . json ( createFetchResult ( { } ) ) ;
2197+ } ,
2198+ { once : true }
2199+ )
2200+ ) ;
2201+ await runWrangler ( `r2 bucket cors delete ${ bucketName } ` ) ;
2202+ expect ( std . out ) . toMatchInlineSnapshot ( `
2203+ "Deleting the CORS configuration for bucket 'my-bucket'...
2204+ CORS configuration deleted for bucket 'my-bucket'."
2205+ ` ) ;
2206+ } ) ;
2207+ } ) ;
2208+ } ) ;
20582209 } ) ;
20592210
20602211 describe ( "r2 object" , ( ) => {
0 commit comments