@@ -145,6 +145,10 @@ pub fn package_router() -> Router<Body, ApiError> {
145
145
"/:package/versions/:version" ,
146
146
util:: auth ( version_update_handler) ,
147
147
)
148
+ . delete (
149
+ "/:package/versions/:version" ,
150
+ util:: auth ( version_delete_handler) ,
151
+ )
148
152
. post (
149
153
"/:package/versions/:version/provenance" ,
150
154
util:: auth ( version_provenance_statements_handler) ,
@@ -942,8 +946,98 @@ pub async fn version_update_handler(
942
946
gzip_encoded : false ,
943
947
} ,
944
948
)
945
- . await
946
- . unwrap ( ) ;
949
+ . await ?;
950
+
951
+ let npm_version_manifest_path =
952
+ crate :: gcs_paths:: npm_version_manifest_path ( & scope, & package) ;
953
+ let npm_version_manifest =
954
+ generate_npm_version_manifest ( db, npm_url, & scope, & package) . await ?;
955
+ let content = serde_json:: to_vec_pretty ( & npm_version_manifest) ?;
956
+ buckets
957
+ . npm_bucket
958
+ . upload (
959
+ npm_version_manifest_path. into ( ) ,
960
+ UploadTaskBody :: Bytes ( content. into ( ) ) ,
961
+ GcsUploadOptions {
962
+ content_type : Some ( "application/json" . into ( ) ) ,
963
+ cache_control : Some ( CACHE_CONTROL_DO_NOT_CACHE . into ( ) ) ,
964
+ gzip_encoded : false ,
965
+ } ,
966
+ )
967
+ . await ?;
968
+
969
+ Ok (
970
+ Response :: builder ( )
971
+ . status ( StatusCode :: NO_CONTENT )
972
+ . body ( Body :: empty ( ) )
973
+ . unwrap ( ) ,
974
+ )
975
+ }
976
+
977
+ #[ instrument(
978
+ name = "DELETE /api/scopes/:scope/packages/:package/versions/:version" ,
979
+ skip( req) ,
980
+ err,
981
+ fields( scope, package, version)
982
+ ) ]
983
+ pub async fn version_delete_handler (
984
+ req : Request < Body > ,
985
+ ) -> ApiResult < Response < Body > > {
986
+ let scope = req. param_scope ( ) ?;
987
+ let package = req. param_package ( ) ?;
988
+ let version = req. param_version ( ) ?;
989
+ Span :: current ( ) . record ( "scope" , field:: display ( & scope) ) ;
990
+ Span :: current ( ) . record ( "package" , field:: display ( & package) ) ;
991
+ Span :: current ( ) . record ( "version" , field:: display ( & version) ) ;
992
+
993
+ let db = req. data :: < Database > ( ) . unwrap ( ) ;
994
+ let buckets = req. data :: < Buckets > ( ) . unwrap ( ) . clone ( ) ;
995
+ let npm_url = & req. data :: < NpmUrl > ( ) . unwrap ( ) . 0 ;
996
+
997
+ let iam = req. iam ( ) ;
998
+ iam. check_admin_access ( ) ?;
999
+
1000
+ let count = db
1001
+ . count_package_dependents (
1002
+ crate :: db:: DependencyKind :: Jsr ,
1003
+ & format ! ( "@{}/{}" , scope, package) ,
1004
+ )
1005
+ . await ?;
1006
+
1007
+ if count > 0 {
1008
+ return Err ( ApiError :: DeleteVersionHasDependents ) ;
1009
+ }
1010
+
1011
+ db. delete_package_version ( & scope, & package, & version)
1012
+ . await ?;
1013
+
1014
+ let path = crate :: gcs_paths:: docs_v1_path ( & scope, & package, & version) ;
1015
+ buckets. docs_bucket . delete_file ( path. into ( ) ) . await ?;
1016
+
1017
+ let path = crate :: gcs_paths:: version_metadata ( & scope, & package, & version) ;
1018
+ buckets. modules_bucket . delete_file ( path. into ( ) ) . await ?;
1019
+
1020
+ let path =
1021
+ crate :: gcs_paths:: file_path_root_directory ( & scope, & package, & version) ;
1022
+ buckets. modules_bucket . delete_directory ( path. into ( ) ) . await ?;
1023
+
1024
+ let package_metadata_path =
1025
+ crate :: gcs_paths:: package_metadata ( & scope, & package) ;
1026
+ let package_metadata = PackageMetadata :: create ( db, & scope, & package) . await ?;
1027
+
1028
+ let content = serde_json:: to_vec_pretty ( & package_metadata) ?;
1029
+ buckets
1030
+ . modules_bucket
1031
+ . upload (
1032
+ package_metadata_path. into ( ) ,
1033
+ UploadTaskBody :: Bytes ( content. into ( ) ) ,
1034
+ GcsUploadOptions {
1035
+ content_type : Some ( "application/json" . into ( ) ) ,
1036
+ cache_control : Some ( CACHE_CONTROL_DO_NOT_CACHE . into ( ) ) ,
1037
+ gzip_encoded : false ,
1038
+ } ,
1039
+ )
1040
+ . await ?;
947
1041
948
1042
let npm_version_manifest_path =
949
1043
crate :: gcs_paths:: npm_version_manifest_path ( & scope, & package) ;
@@ -4093,4 +4187,78 @@ ggHohNAjhbzDaY2iBW/m3NC5dehGUP4T2GBo/cwGhg==
4093
4187
assert_eq ! ( tasks. len( ) , 1 ) ;
4094
4188
assert_eq ! ( tasks[ 0 ] . id, task2. id) ;
4095
4189
}
4190
+
4191
+ #[ tokio:: test]
4192
+ async fn delete_version ( ) {
4193
+ let mut t = TestSetup :: new ( ) . await ;
4194
+ let staff_token = t. staff_user . token . clone ( ) ;
4195
+
4196
+ // unpublished package
4197
+ let mut resp = t
4198
+ . http ( )
4199
+ . get ( "/api/scopes/scope/packages/foo/versions/0.0.1/dependencies/graph" )
4200
+ . call ( )
4201
+ . await
4202
+ . unwrap ( ) ;
4203
+ resp
4204
+ . expect_err_code ( StatusCode :: NOT_FOUND , "packageVersionNotFound" )
4205
+ . await ;
4206
+
4207
+ let task = process_tarball_setup ( & t, create_mock_tarball ( "ok" ) ) . await ;
4208
+ assert_eq ! ( task. status, PublishingTaskStatus :: Success , "{:?}" , task) ;
4209
+
4210
+ // Now publish a package that has a few deps
4211
+ let package_name = PackageName :: try_from ( "bar" ) . unwrap ( ) ;
4212
+ let version = Version :: try_from ( "1.2.3" ) . unwrap ( ) ;
4213
+ let task = process_tarball_setup2 (
4214
+ & t,
4215
+ create_mock_tarball ( "depends_on_ok" ) ,
4216
+ & package_name,
4217
+ & version,
4218
+ false ,
4219
+ )
4220
+ . await ;
4221
+ assert_eq ! ( task. status, PublishingTaskStatus :: Success , "{:?}" , task) ;
4222
+
4223
+ let mut resp = t
4224
+ . http ( )
4225
+ . delete ( "/api/scopes/scope/packages/foo/versions/0.0.1" )
4226
+ . token ( Some ( & staff_token) )
4227
+ . call ( )
4228
+ . await
4229
+ . unwrap ( ) ;
4230
+ resp
4231
+ . expect_err_code ( StatusCode :: BAD_REQUEST , "deleteVersionHasDependents" )
4232
+ . await ;
4233
+
4234
+ let mut resp = t
4235
+ . http ( )
4236
+ . delete ( "/api/scopes/scope/packages/bar/versions/1.2.3" )
4237
+ . token ( Some ( & staff_token) )
4238
+ . call ( )
4239
+ . await
4240
+ . unwrap ( ) ;
4241
+ resp. expect_ok_no_content ( ) . await ;
4242
+
4243
+ let mut resp = t
4244
+ . http ( )
4245
+ . delete ( "/api/scopes/scope/packages/foo/versions/0.0.1" )
4246
+ . token ( Some ( & staff_token) )
4247
+ . call ( )
4248
+ . await
4249
+ . unwrap ( ) ;
4250
+ resp. expect_ok_no_content ( ) . await ;
4251
+
4252
+ let package_name = PackageName :: try_from ( "foo" ) . unwrap ( ) ;
4253
+ let version = Version :: try_from ( "0.0.1" ) . unwrap ( ) ;
4254
+ let task = process_tarball_setup2 (
4255
+ & t,
4256
+ create_mock_tarball ( "ok" ) ,
4257
+ & package_name,
4258
+ & version,
4259
+ false ,
4260
+ )
4261
+ . await ;
4262
+ assert_eq ! ( task. status, PublishingTaskStatus :: Failure , "{:?}" , task) ;
4263
+ }
4096
4264
}
0 commit comments