99 ********************************************************************************/
1010package org .eclipse .openvsx ;
1111
12+ import jakarta .persistence .EntityManager ;
1213import jakarta .transaction .Transactional ;
1314import jakarta .transaction .Transactional .TxType ;
1415import org .apache .commons .lang3 .StringUtils ;
16+ import org .eclipse .openvsx .admin .RemoveFileJobRequest ;
1517import org .eclipse .openvsx .cache .CacheService ;
1618import org .eclipse .openvsx .entities .*;
19+ import org .eclipse .openvsx .json .ResultJson ;
20+ import org .eclipse .openvsx .json .TargetPlatformVersionJson ;
1721import org .eclipse .openvsx .publish .PublishExtensionVersionHandler ;
1822import org .eclipse .openvsx .repositories .RepositoryService ;
1923import org .eclipse .openvsx .search .SearchUtilService ;
2024import org .eclipse .openvsx .util .ErrorResultException ;
25+ import org .eclipse .openvsx .util .NamingUtil ;
2126import org .eclipse .openvsx .util .TempFile ;
2227import org .eclipse .openvsx .util .TimeUtil ;
28+ import org .jobrunr .scheduling .JobRequestScheduler ;
2329import org .springframework .beans .factory .annotation .Value ;
2430import org .springframework .http .HttpStatus ;
2531import org .springframework .stereotype .Component ;
3036import java .io .InputStream ;
3137import java .nio .file .Files ;
3238import java .time .LocalDateTime ;
39+ import java .util .ArrayList ;
3340import java .util .LinkedHashSet ;
41+ import java .util .List ;
42+ import java .util .Objects ;
43+ import java .util .stream .Collectors ;
3444
3545@ Component
3646public class ExtensionService {
3747
3848 private static final int MAX_CONTENT_SIZE = 512 * 1024 * 1024 ;
3949
50+ private final EntityManager entityManager ;
4051 private final RepositoryService repositories ;
4152 private final SearchUtilService search ;
4253 private final CacheService cache ;
4354 private final PublishExtensionVersionHandler publishHandler ;
55+ private final JobRequestScheduler scheduler ;
4456
4557 @ Value ("${ovsx.publishing.require-license:false}" )
4658 boolean requireLicense ;
4759
4860 public ExtensionService (
61+ EntityManager entityManager ,
4962 RepositoryService repositories ,
5063 SearchUtilService search ,
5164 CacheService cache ,
52- PublishExtensionVersionHandler publishHandler
65+ PublishExtensionVersionHandler publishHandler ,
66+ JobRequestScheduler scheduler
5367 ) {
68+ this .entityManager = entityManager ;
5469 this .repositories = repositories ;
5570 this .search = search ;
5671 this .cache = cache ;
5772 this .publishHandler = publishHandler ;
73+ this .scheduler = scheduler ;
5874 }
5975
6076 @ Transactional
@@ -152,4 +168,85 @@ public void reactivateExtensions(UserData user) {
152168 updateExtension (extension );
153169 }
154170 }
171+
172+ @ Transactional (rollbackOn = ErrorResultException .class )
173+ public ResultJson deleteExtension (
174+ String namespaceName ,
175+ String extensionName ,
176+ List <TargetPlatformVersionJson > targetVersions ,
177+ UserData user
178+ ) throws ErrorResultException {
179+ var results = new ArrayList <ResultJson >();
180+ if (repositories .isDeleteAllVersions (namespaceName , extensionName , targetVersions , user )) {
181+ var extension = repositories .findExtension (extensionName , namespaceName );
182+ results .add (deleteExtension (extension ));
183+ } else {
184+ for (var targetVersion : targetVersions ) {
185+ var extVersion = repositories .findVersion (user , targetVersion .version (), targetVersion .targetPlatform (), extensionName , namespaceName );
186+ if (extVersion == null ) {
187+ var message = "Extension not found: " + NamingUtil .toLogFormat (namespaceName , extensionName , targetVersion .targetPlatform (), targetVersion .version ());
188+ throw new ErrorResultException (message , HttpStatus .NOT_FOUND );
189+ }
190+
191+ return deleteExtension (extVersion );
192+ }
193+ }
194+
195+ var result = new ResultJson ();
196+ result .setError (results .stream ().map (ResultJson ::getError ).filter (Objects ::nonNull ).collect (Collectors .joining ("\n " )));
197+ result .setSuccess (results .stream ().map (ResultJson ::getSuccess ).filter (Objects ::nonNull ).collect (Collectors .joining ("\n " )));
198+ return result ;
199+ }
200+
201+ protected ResultJson deleteExtension (Extension extension ) throws ErrorResultException {
202+ var bundledRefs = repositories .findBundledExtensionsReference (extension );
203+ if (!bundledRefs .isEmpty ()) {
204+ throw new ErrorResultException ("Extension " + NamingUtil .toExtensionId (extension )
205+ + " is bundled by the following extension packs: "
206+ + bundledRefs .stream ()
207+ .map (NamingUtil ::toFileFormat )
208+ .collect (Collectors .joining (", " )));
209+ }
210+ var dependRefs = repositories .findDependenciesReference (extension );
211+ if (!dependRefs .isEmpty ()) {
212+ throw new ErrorResultException ("The following extensions have a dependency on " + NamingUtil .toExtensionId (extension ) + ": "
213+ + dependRefs .stream ()
214+ .map (NamingUtil ::toFileFormat )
215+ .collect (Collectors .joining (", " )));
216+ }
217+
218+ cache .evictExtensionJsons (extension );
219+ for (var extVersion : repositories .findVersions (extension )) {
220+ removeExtensionVersion (extVersion );
221+ }
222+ for (var review : repositories .findAllReviews (extension )) {
223+ entityManager .remove (review );
224+ }
225+
226+ var deprecatedExtensions = repositories .findDeprecatedExtensions (extension );
227+ for (var deprecatedExtension : deprecatedExtensions ) {
228+ deprecatedExtension .setReplacement (null );
229+ cache .evictExtensionJsons (deprecatedExtension );
230+ }
231+
232+ entityManager .remove (extension );
233+ search .removeSearchEntry (extension );
234+
235+ return ResultJson .success ("Deleted " + NamingUtil .toExtensionId (extension ));
236+ }
237+
238+ protected ResultJson deleteExtension (ExtensionVersion extVersion ) {
239+ var extension = extVersion .getExtension ();
240+ removeExtensionVersion (extVersion );
241+ extension .getVersions ().remove (extVersion );
242+ updateExtension (extension );
243+
244+ return ResultJson .success ("Deleted " + NamingUtil .toLogFormat (extVersion ));
245+ }
246+
247+ private void removeExtensionVersion (ExtensionVersion extVersion ) {
248+ repositories .findFiles (extVersion ).map (RemoveFileJobRequest ::new ).forEach (scheduler ::enqueue );
249+ repositories .deleteFiles (extVersion );
250+ entityManager .remove (extVersion );
251+ }
155252}
0 commit comments