001package org.nasdanika.models.gitlab.util;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Collections;
006import java.util.HashMap;
007import java.util.List;
008import java.util.Map;
009import java.util.Objects;
010import java.util.concurrent.CompletableFuture;
011import java.util.concurrent.Executor;
012import java.util.function.Function;
013import java.util.logging.Handler;
014import java.util.logging.Level;
015import java.util.logging.Logger;
016
017import org.eclipse.emf.common.util.EList;
018import org.eclipse.emf.common.util.EMap;
019import org.gitlab4j.api.Constants.AutoDevopsDeployStrategy;
020import org.gitlab4j.api.Constants.BuildGitStrategy;
021import org.gitlab4j.api.Constants.SquashOption;
022import org.gitlab4j.api.GitLabApi;
023import org.gitlab4j.api.GitLabApiException;
024import org.gitlab4j.api.GroupApi;
025import org.gitlab4j.api.Pager;
026import org.gitlab4j.api.ProjectApi;
027import org.gitlab4j.api.ProjectLicense;
028import org.gitlab4j.api.RepositoryApi;
029import org.gitlab4j.api.RepositoryFileApi;
030import org.gitlab4j.api.models.AbstractUser;
031import org.gitlab4j.api.models.Branch;
032import org.gitlab4j.api.models.Commit;
033import org.gitlab4j.api.models.Contributor;
034import org.gitlab4j.api.models.CustomAttribute;
035import org.gitlab4j.api.models.Group;
036import org.gitlab4j.api.models.Group.Statistics;
037import org.gitlab4j.api.models.GroupFilter;
038import org.gitlab4j.api.models.Member;
039import org.gitlab4j.api.models.Owner;
040import org.gitlab4j.api.models.Permissions;
041import org.gitlab4j.api.models.Project;
042import org.gitlab4j.api.models.ProjectAccess;
043import org.gitlab4j.api.models.ProjectSharedGroup;
044import org.gitlab4j.api.models.ProjectStatistics;
045import org.gitlab4j.api.models.RepositoryFile;
046import org.gitlab4j.api.models.TreeItem;
047import org.gitlab4j.api.models.Visibility;
048import org.nasdanika.common.ProgressMonitor;
049import org.nasdanika.common.Status;
050import org.nasdanika.models.gitlab.GitLab;
051import org.nasdanika.models.gitlab.GitLabFactory;
052import org.nasdanika.models.gitlab.MergeMethod;
053
054/**
055 * Loads data into the model using {@link GitLabApi}.
056 * @deprecated Migrate to Loader
057 */
058@Deprecated
059public class LoaderOld implements AutoCloseable {
060        
061        private static final String ROOT_PATH = "/";
062        private GitLabApi gitLabApi;
063        private GitLabFactory factory = GitLabFactory.eINSTANCE;
064        private int groupsPageSize = 10;
065        
066        public int getGroupsPageSize() {
067                return groupsPageSize;
068        }
069        
070        /**
071         * Page size for retrieving groups.
072         * @param groupsPageSize
073         */
074        public void setGroupsPageSize(int groupsPageSize) {
075                this.groupsPageSize = groupsPageSize;
076        }
077        
078        // Caller thread executor
079        private Executor executor = r -> r.run();
080        
081        public Executor getExecutor() {
082                return executor;
083        }
084        
085        /**
086         * @param executor To execute loading in parallel
087         */
088        public void setExecutor(Executor executor) {
089                this.executor = executor;
090        }
091        
092        /**
093         * Access to the API for configuration. 
094         * @return
095         */
096        public GitLabApi getGitLabApi() {
097                return gitLabApi;
098        }
099        
100        public LoaderOld(String hostUrl, String accessToken) {
101                this(new GitLabApi(hostUrl, accessToken));
102        }       
103        
104        public LoaderOld(GitLabApi gitLabApi) {
105                this(gitLabApi, new ThrottlingHandler());
106        }
107        
108        /**
109         * @param clientRateLimitWindow Client rate window in milliseconds. Client rate limit is enforced if this value and clientRateLimit are positive.
110         * @param clientRateLimit Client rate limit per rate window. Client rate limit is enforced if this value and clientRateLimitWindow are positive.
111         */     
112        public LoaderOld(
113                        String hostUrl, 
114                        String accessToken, 
115                        long clientRateLimitWindow,
116                        int clientRateLimit) {
117                this(new GitLabApi(hostUrl, accessToken), clientRateLimitWindow, clientRateLimit);
118        }       
119        
120        /**
121         * @param clientRateLimitWindow Client rate window in milliseconds. Client rate limit is enforced if this value and clientRateLimit are positive.
122         * @param clientRateLimit Client rate limit per rate window. Client rate limit is enforced if this value and clientRateLimitWindow are positive.
123         */     
124        public LoaderOld(
125                        GitLabApi gitLabApi,
126                        long clientRateLimitWindow,
127                        int clientRateLimit) {
128                this(gitLabApi, new ThrottlingHandler(clientRateLimitWindow, clientRateLimit));
129        }       
130        
131        public LoaderOld(GitLabApi gitLabApi, Handler throttlingHandler) {
132                if (throttlingHandler != null) {                        
133                        Level level = Level.FINE;
134                        throttlingHandler.setLevel(level);
135                        Logger logger = Logger.getLogger(GitLabApi.class.getName());
136                        Level loggerLevel = logger.getLevel();
137                        if (loggerLevel == null || loggerLevel.intValue() > level.intValue()) {
138                                logger.setLevel(level);
139                        }
140                        logger.addHandler(throttlingHandler);
141                        gitLabApi.enableRequestResponseLogging();
142                }
143                
144                this.gitLabApi = gitLabApi;
145        }
146        
147        public GitLabFactory getFactory() {
148                return factory;
149        }
150        
151        public void setFactory(GitLabFactory factory) {
152                this.factory = factory;
153        }
154        
155        /**
156         * Loads groups, their projects, sub-groups, members and other related objects.
157         * @param progressMonitor
158         * @return Populated {@link GitLab} instance.
159         * @throws GitLabApiException 
160         */
161        public GitLab loadGitLabGroups(ProgressMonitor progressMonitor) throws GitLabApiException {
162                GitLab ret = getFactory().createGitLab();               
163                ret.getGroups().addAll(loadGroups(progressMonitor));                            
164                return ret;
165        }
166        
167        protected org.nasdanika.models.gitlab.ProjectLicense loadProjectLicense(ProjectLicense apiLicense, ProgressMonitor progressMonitor) {
168                org.nasdanika.models.gitlab.ProjectLicense modelLicense = getFactory().createProjectLicense();
169                modelLicense.setHtmlUrl(apiLicense.getHtmlUrl());
170                modelLicense.setKey(apiLicense.getKey());
171                modelLicense.setName(apiLicense.getName());
172                modelLicense.setNickname(apiLicense.getNickname());
173                modelLicense.setSourceUrl(apiLicense.getSourceUrl());
174                return modelLicense;
175        }
176
177        protected void populateAbstractUser(AbstractUser<?> apiUser, org.nasdanika.models.gitlab.AbstractUser user) {
178                user.setAvatarUrl(apiUser.getAvatarUrl());
179                user.setCreatedAt(apiUser.getCreatedAt());
180                user.setEMail(apiUser.getEmail());
181                user.setId(apiUser.getId());
182                user.setName(apiUser.getName());
183                user.setState(apiUser.getState());
184                user.setUserName(apiUser.getUsername());
185                user.setWebUrl(apiUser.getWebUrl());
186        }
187        
188        /**
189         * This implementation returns a new instance of {@link GroupFilter}.
190         * Override to customize.
191         * @return
192         */
193        protected GroupFilter getGroupFilter() {
194                return new GroupFilter();
195        }
196
197        /**
198         * @param progressMonitor
199         * @return Top-level (root) groups with sub-groups mounted under them
200         * @throws GitLabApiException
201         */
202        public List<org.nasdanika.models.gitlab.Group> loadGroups(ProgressMonitor progressMonitor) throws GitLabApiException {
203                Map<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupMap = Collections.synchronizedMap(new HashMap<>());
204                Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider = id -> groupMap.computeIfAbsent(id, _id -> new CompletableFuture<>());
205                
206                Map<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectMap = Collections.synchronizedMap(new HashMap<>());
207                Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider = id -> projectMap.computeIfAbsent(id, _id -> new CompletableFuture<>());
208
209                List<org.nasdanika.models.gitlab.Group> rootGroups = Collections.synchronizedList(new ArrayList<>()); 
210                List<org.nasdanika.models.gitlab.Group> childGroups = Collections.synchronizedList(new ArrayList<>()); 
211                
212                Collection<CompletableFuture<Map.Entry<Group, org.nasdanika.models.gitlab.Group>>> groupCompletableFutures = Collections.synchronizedCollection(new ArrayList<>()); // Synchronizing just in case
213                try (ProgressMonitor groupsMonitor = progressMonitor.split("Loading groups", 1)) {
214                        GroupApi groupApi = gitLabApi.getGroupApi();
215                        Pager<Group> groupPager = groupApi.getGroups(getGroupFilter(), getGroupsPageSize());
216                        int pageNum = 0;
217                        while (groupPager.hasNext()) {
218                                ++pageNum;
219                                double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
220                                try (ProgressMonitor groupPageMonitor = groupsMonitor.split("Group page " + pageNum, monitorSize)) {
221                                        List<Group> groups = groupPager.next();
222                                        try (ProgressMonitor scaledGroupsMonitor = groupPageMonitor.scale(groups.size() + 1)) {
223                                                scaledGroupsMonitor.worked(Status.INFO, 1, "Retrieved " + groups.size() + " groups");
224                                                for (org.gitlab4j.api.models.Group group: groups) {
225                                                        try (ProgressMonitor groupMonitor = scaledGroupsMonitor.split("Loading group " + group.getName() + " " + group.getId(), 1, group)) {
226                                                                CompletableFuture<Map.Entry<Group,org.nasdanika.models.gitlab.Group>> modelGroupCompletableFuture = new CompletableFuture<>();
227                                                                groupCompletableFutures.add(modelGroupCompletableFuture);
228                                                                executor.execute(() -> { 
229                                                                        org.nasdanika.models.gitlab.Group modelGroup = loadGroup(
230                                                                                        group, 
231                                                                                        groupApi, 
232                                                                                        groupProvider,  
233                                                                                        projectProvider,
234                                                                                        groupMonitor);
235                                                                        
236                                                                        modelGroupCompletableFuture.complete(Map.entry(group, modelGroup));
237                                                                });
238                                                        }
239                                                }
240                                        }
241                                }
242                        }
243                }
244                
245                for (CompletableFuture<Map.Entry<Group, org.nasdanika.models.gitlab.Group>> gcf: groupCompletableFutures) {
246                        Map.Entry<Group,org.nasdanika.models.gitlab.Group> groupEntry = gcf.join();
247                        Group group = groupEntry.getKey();
248                        org.nasdanika.models.gitlab.Group modelGroup = groupEntry.getValue();
249                        if (!groupProvider.apply(group.getId()).complete(modelGroup)) {
250                                progressMonitor.worked(1, "Group completable future already completed for " + group.getId() + " " + group.getFullName(), group);
251                        }
252                        Long parentId = group.getParentId();
253                        if (parentId == null) {
254                                rootGroups.add(modelGroup);
255                        } else {
256                                groupProvider.apply(parentId).thenAccept(pg -> {
257                                        EList<org.nasdanika.models.gitlab.Group> subGroups = pg.getSubGroups();
258                                        synchronized (LoaderOld.this) { // Global synchronization - to avoid concurrency issues in notifications. Not needed here, just in case.
259                                                subGroups.add(modelGroup);
260                                        }
261                                });                                     
262                        }                       
263                }
264                
265                long incomplete = groupMap.values().stream().filter(cf -> !cf.isDone()).count();
266                if (incomplete > 0) {
267                        progressMonitor.worked(1, "There are incomplete set parent futures (orphan child groups): " + incomplete);
268                        for (org.nasdanika.models.gitlab.Group childGroup: childGroups) {
269                                rootGroups.add(childGroup);
270                        }
271                }
272                long exceptionally = groupMap.values().stream().filter(cf -> cf.isCompletedExceptionally()).count();
273                if (exceptionally > 0) {
274                        progressMonitor.worked(1, "There are exceptionally completed set parent futures: " + exceptionally);
275                        for (CompletableFuture<org.nasdanika.models.gitlab.Group> cf: groupMap.values()) {
276                                if (cf.isCompletedExceptionally()) {
277                                        cf.exceptionally(th -> {
278                                                progressMonitor.worked(1, "Exceptional completion: " + th, th);
279                                                return null;
280                                        });
281                                }
282                        }
283                }               
284                return rootGroups;
285        }
286
287        protected org.nasdanika.models.gitlab.Group createGroup(
288                        org.gitlab4j.api.models.Group group, 
289                        GroupApi groupApi,
290                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider,
291                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider, 
292                        ProgressMonitor progressMonitor) {
293                
294                return getFactory().createGroup();
295        }
296        
297        protected org.nasdanika.models.gitlab.Group loadGroup(
298                        org.gitlab4j.api.models.Group group, 
299                        GroupApi groupApi,
300                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider,
301                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider, 
302                        ProgressMonitor progressMonitor) {
303                
304                org.nasdanika.models.gitlab.Group modelGroup = createGroup(group, groupApi, groupProvider, projectProvider, progressMonitor); 
305                modelGroup.setAvatarUrl(group.getAvatarUrl());
306                modelGroup.setCreatedAt(group.getCreatedAt());
307                modelGroup.setDescription(group.getDescription());
308                modelGroup.setFullName(group.getFullName());
309                modelGroup.setFullPath(group.getFullPath());
310                modelGroup.setId(group.getId());
311                Statistics stats = group.getStatistics();
312                if (stats != null) {
313                        modelGroup.setJobArtifactsSize(stats.getJobArtifactsSize());
314                        modelGroup.setLfsObjectsSize(stats.getLfsObjectsSize());
315                        modelGroup.setRepositorySize(stats.getRepositorySize());
316                        modelGroup.setStorageSize(stats.getStorageSize());
317                }
318                modelGroup.setLfsEnabled(group.getLfsEnabled());
319                modelGroup.setName(group.getName());
320                modelGroup.setPath(group.getPath());
321                modelGroup.setVisibility(org.nasdanika.models.gitlab.Visibility.get(group.getVisibility().ordinal()));
322                modelGroup.setWebUrl(group.getWebUrl());
323                
324                EList<org.nasdanika.models.gitlab.Project> modelGroupProjects = modelGroup.getProjects();
325                
326                try {
327                        List<Project> groupProjects = groupApi.getProjects(group.getId());
328                        List<Member> groupMembers = groupApi.getMembers(group.getId());
329                        try (ProgressMonitor scaledGroupMonitor = progressMonitor.scale(1 + groupProjects.size() + groupMembers.size())) {
330                                scaledGroupMonitor.worked(Status.INFO, 1, "Retrieved " + groupProjects.size() + " projects and " + groupMembers.size() + " members");                                                   
331                                for (org.gitlab4j.api.models.Project project: groupProjects) {
332                                        try (ProgressMonitor projectMonitor = scaledGroupMonitor.split("Loading project " + project.getName() + " " + project.getId(), 1, project)) {
333                                                if (Objects.equals(project.getNamespace().getId(), group.getId())) {
334                                                        org.nasdanika.models.gitlab.Project modelProject = loadProject(
335                                                                        project, 
336                                                                        groupProvider, 
337                                                                        projectProvider,
338                                                                        projectMonitor);
339                                                        modelGroupProjects.add(modelProject);
340                                                }
341                                        }
342                                }
343                                EList<org.nasdanika.models.gitlab.Member> modelGroupMembers = modelGroup.getMembers();
344                                for (org.gitlab4j.api.models.Member member: groupMembers) {
345                                        try (ProgressMonitor memberMonitor = scaledGroupMonitor.split("Loading member " + member.getName() + " " + member.getId(), 1, member)) {                                
346                                                org.nasdanika.models.gitlab.Member modelMember = loadMember(member, memberMonitor);
347                                                modelGroupMembers.add(modelMember);
348                                        }
349                                }
350                        }
351        
352                } catch (GitLabApiException e) {
353                        progressMonitor.worked(Status.ERROR, 1, "Failed to load group", group, e);                      
354                }
355                return modelGroup;
356        }
357        
358        /**
359         * Creates a new instance of model project. Called by loadProject(). 
360         * This implementation calls getFactory().createProject(). Override to customize creation. 
361         * E.g. create a subclass of Project, load a project from a prototypes with some information pre-filled, ...
362         * @param project
363         * @param groupProvider
364         * @param projectProvider
365         * @param progressMonitor
366         * @return
367         */
368        protected org.nasdanika.models.gitlab.Project createProject(
369                        org.gitlab4j.api.models.Project project, 
370                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider,
371                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider,                 
372                        ProgressMonitor progressMonitor) {
373                return getFactory().createProject();
374        }       
375        
376        protected org.nasdanika.models.gitlab.Owner createOwner(Owner owner, ProgressMonitor progressMonitor) {
377                return getFactory().createOwner();
378        }
379        
380        protected org.nasdanika.models.gitlab.Branch createBranch(Branch branch, ProgressMonitor progressMonitor) {
381                return getFactory().createBranch();
382        }
383        
384        protected org.nasdanika.models.gitlab.Contributor createContributor(Contributor contributor, ProgressMonitor progressMonitor) {
385                return getFactory().createContributor();
386        }
387        
388        protected org.nasdanika.models.gitlab.Project loadProject(
389                        org.gitlab4j.api.models.Project project, 
390                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider,
391                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider,                 
392                        ProgressMonitor progressMonitor) {
393                                
394                org.nasdanika.models.gitlab.Project modelProject = createProject(project, groupProvider, projectProvider, progressMonitor);
395                
396                modelProject.setId(project.getId());
397                modelProject.setApprovalsBeforeMerge(project.getApprovalsBeforeMerge());
398                modelProject.setArchived(project.getArchived());
399                modelProject.setAvatarUrl(project.getAvatarUrl());
400                modelProject.setContainerRegistryEnabled(project.getContainerRegistryEnabled());
401                modelProject.setCreatedAt(project.getCreatedAt());
402                modelProject.setCreatorId(project.getCreatorId());
403                modelProject.setDefaultBranch(project.getDefaultBranch());
404                modelProject.setDescription(project.getDescription());          
405                modelProject.setForksCount(project.getForksCount());            
406
407                Project forkedFrom = project.getForkedFromProject();
408                if (forkedFrom != null) {
409                        projectProvider.apply(forkedFrom.getId()).thenAccept(modelProject::setForkedFrom);
410                }
411                
412                modelProject.setHttpUrlToRepo(project.getHttpUrlToRepo());              
413                modelProject.setIsPublic(project.getPublic());          
414                modelProject.setIssuesEnabled(project.getIssuesEnabled());              
415                modelProject.setJobsEnabled(project.getJobsEnabled());          
416                modelProject.setLastsActivityAt(project.getLastActivityAt());           
417                modelProject.setLfsEnabled(project.getLfsEnabled());            
418                modelProject.setMergeMethod(MergeMethod.get(project.getMergeMethod().ordinal()));               
419                modelProject.setMergeRequestsEnabled(project.getMergeRequestsEnabled());                
420                modelProject.setName(project.getName());                
421                modelProject.setNameWithNamespace(project.getNameWithNamespace());              
422                modelProject.setOnlyAllowMergeIfAllDiscussionsAreResolved(project.getOnlyAllowMergeIfAllDiscussionsAreResolved());              
423                modelProject.setOpenIssuesCount(project.getOpenIssuesCount());          
424                
425                Owner owner = project.getOwner();
426                if (owner != null) {
427                        org.nasdanika.models.gitlab.Owner modelOwner = createOwner(owner, progressMonitor);
428                        populateAbstractUser(owner, modelOwner);
429                        modelProject.setOwner(modelOwner);
430                }
431                
432                modelProject.setPath(project.getPath());
433                modelProject.setPathWithNamespace(project.getPathWithNamespace());
434                
435                Permissions permissions = project.getPermissions();
436                if (permissions != null) {
437                        ProjectAccess groupAccess = permissions.getGroupAccess();
438                        if (groupAccess != null) {
439                                org.nasdanika.models.gitlab.ProjectAccess modelGroupAccess = getFactory().createProjectAccess();
440                                modelGroupAccess.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(groupAccess.getAccessLevel().value));
441                                modelProject.setGroupAccess(modelGroupAccess);
442                        }
443                        ProjectAccess projectAccess = permissions.getProjectAccess();
444                        if (projectAccess != null) {
445                                org.nasdanika.models.gitlab.ProjectAccess modelProjectAccess = getFactory().createProjectAccess();
446                                modelProjectAccess.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(projectAccess.getAccessLevel().value));
447                                modelProject.setGroupAccess(modelProjectAccess);
448                        }
449                }
450                
451                modelProject.setPublicJobs(project.getPublicJobs());
452                modelProject.setRepositoryStorage(project.getRepositoryStorage());
453                modelProject.setRequestAccessEnabled(project.getRequestAccessEnabled());
454                modelProject.setRunnersToken(project.getRunnersToken());
455                modelProject.setSharedRunnersEnabled(project.getSharedRunnersEnabled());
456                
457                List<ProjectSharedGroup> sharedGroups = project.getSharedWithGroups();
458                if (sharedGroups != null) {
459                        for (ProjectSharedGroup sg: sharedGroups) {
460                                org.nasdanika.models.gitlab.ProjectSharedGroup psg = getFactory().createProjectSharedGroup();
461                                org.gitlab4j.api.models.AccessLevel accessLevel = sg.getGroupAccessLevel();
462                                if (accessLevel != null) {
463                                        psg.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(accessLevel.value));
464                                }
465                                psg.setFullPath(sg.getGroupFullPath());
466                                psg.setId(sg.getGroupId());
467                                psg.setName(sg.getGroupName());
468                                groupProvider.apply(sg.getGroupId()).thenAccept(psg::setGroup);
469                        }
470                }
471                                
472                modelProject.setSnippetsEnabled(project.getSnippetsEnabled());
473                modelProject.setSshUrlToRepo(project.getSshUrlToRepo());
474                modelProject.setStarCount(project.getStarCount());
475
476                List<String> tags = project.getTagList();
477                if (tags != null) {
478                        modelProject.getTags().addAll(tags);
479                }
480                
481                modelProject.setVisibilityLevel(project.getVisibilityLevel());
482                Visibility projectVisibility = project.getVisibility();
483                if (projectVisibility != null) {
484                        modelProject.setVisibility(org.nasdanika.models.gitlab.Visibility.get(projectVisibility.ordinal()));
485                }
486                modelProject.setWallEnabled(project.getWallEnabled());
487                modelProject.setWebUrl(project.getWebUrl());
488                modelProject.setWikiEnabled(project.getWikiEnabled());
489                modelProject.setPrintingMergeRequestLinkEnabled(project.getPrintingMergeRequestLinkEnabled());
490                modelProject.setResolveOutdatedDiffDiscussions(project.getResolveOutdatedDiffDiscussions());
491                
492                ProjectStatistics projectStatistics = project.getStatistics();
493                if (projectStatistics != null) {
494                        org.nasdanika.models.gitlab.ProjectStatistics modelProjectStatistics = getFactory().createProjectStatistics();
495                        modelProjectStatistics.setCommitCount(projectStatistics.getCommitCount());
496                        modelProjectStatistics.setJobArtifactsSize(projectStatistics.getJobArtifactsSize());
497                        modelProjectStatistics.setLfsObjectsSize(projectStatistics.getLfsObjectsSize());
498                        modelProjectStatistics.setPackagesSize(projectStatistics.getPackagesSize());
499                        modelProjectStatistics.setRepositorySize(projectStatistics.getRepositorySize());
500                        modelProjectStatistics.setStorageSize(projectStatistics.getStorageSize());
501                        modelProjectStatistics.setWikiSize(projectStatistics.getWikiSize());
502                        
503                        modelProject.setStatistics(modelProjectStatistics);
504                }
505                
506                modelProject.setInitializeWithReadme(null);
507                modelProject.setPackagesEnabled(null);
508                modelProject.setEmptyRepo(null);
509                modelProject.setLicenseUrl(null);
510                
511                ProjectLicense apiLicense = project.getLicense();
512                if (apiLicense != null) {
513                        modelProject.setLicense(loadProjectLicense(apiLicense, progressMonitor));
514                }
515
516                List<CustomAttribute> apiCustomAttributes = project.getCustomAttributes();
517                if (apiCustomAttributes != null) {
518                        EMap<String, String> modelCustomAttributes = modelProject.getCustomAttributes();
519                        apiCustomAttributes.forEach(ca -> modelCustomAttributes.put(ca.getKey(), ca.getValue()));
520                }
521                
522                modelProject.setBuildCoverageRegex(project.getBuildCoverageRegex());
523                
524                BuildGitStrategy buildGitStrategy = project.getBuildGitStrategy();
525                if (buildGitStrategy != null) {
526                        modelProject.setBuildGitStrategy(org.nasdanika.models.gitlab.BuildGitStrategy.get(buildGitStrategy.ordinal()));
527                }
528                
529                modelProject.setReadmeUrl(project.getReadmeUrl());
530                modelProject.setCanCreateMergeRequestIn(project.getCanCreateMergeRequestIn());
531                
532                org.gitlab4j.api.models.ImportStatus.Status importStatus = project.getImportStatus();
533                if (importStatus != null) {
534                        modelProject.setImportStatus(org.nasdanika.models.gitlab.Status.get(importStatus.ordinal()));
535                }
536                modelProject.setCiDefaultGitDepth(project.getCiDefaultGitDepth());
537                modelProject.setCiForwardDeploymentEnabled(project.getCiForwardDeploymentEnabled());
538                modelProject.setCiConfigPath(project.getCiConfigPath());
539                modelProject.setRemoveSourceBranchAfterMerge(project.getRemoveSourceBranchAfterMerge());
540                modelProject.setAutoDevopsEnabled(project.getAutoDevopsEnabled());
541                
542                AutoDevopsDeployStrategy autoDevopsDeployStrategy = project.getAutoDevopsDeployStrategy();
543                if (autoDevopsDeployStrategy != null) {
544                        modelProject.setAutoDevopsDeployStrategy(org.nasdanika.models.gitlab.AutoDevopsDeployStrategy.get(autoDevopsDeployStrategy.ordinal()));
545                }
546                
547                modelProject.setAutocloseReferencedIssues(project.getAutocloseReferencedIssues());
548                modelProject.setEmailsDisabled(project.getEmailsDisabled());
549                modelProject.setSuggestionCommitMessage(project.getSuggestionCommitMessage());
550                
551                SquashOption squashOption = project.getSquashOption();
552                if (squashOption != null) {
553                        modelProject.setSquashOption(org.nasdanika.models.gitlab.SquashOption.get(squashOption.ordinal()));
554                }               
555                
556                RepositoryApi repoApi = gitLabApi.getRepositoryApi();
557                try {
558                        List<Branch> branches = repoApi.getBranches(project.getId());
559                        if (branches != null) {
560                                EList<org.nasdanika.models.gitlab.Branch> modelBranches = modelProject.getBranches();
561                                for (Branch branch: branches) {
562                                        org.nasdanika.models.gitlab.Branch modelBranch = createBranch(branch, progressMonitor);
563                                        modelBranch.setCanPush(branch.getCanPush());
564                                        
565                                        Commit commit = branch.getCommit();
566                                        if (commit != null) {
567                                                modelBranch.setCommitDate(commit.getCommittedDate());
568                                        }
569                                        
570                                        modelBranch.setDevelopersCanMerge(branch.getDevelopersCanMerge());
571                                        modelBranch.setDevelopersCanPush(branch.getDevelopersCanPush());
572                                        modelBranch.setIsDefault(branch.getDefault());
573                                        modelBranch.setIsProtected(branch.getProtected());
574                                        modelBranch.setMerged(branch.getMerged());
575                                        modelBranch.setName(branch.getName());
576                                        modelBranch.setWebUrl(branch.getWebUrl());
577                                        modelBranches.add(modelBranch);
578                                        
579                                        if (isLoadPath(modelProject, modelBranch, ROOT_PATH)) {
580                                                modelBranch.getTreeItems().addAll(
581                                                                loadTree(
582                                                                                modelProject, 
583                                                                                modelBranch, 
584                                                                                ROOT_PATH, 
585                                                                                groupProvider, 
586                                                                                projectProvider,
587                                                                                progressMonitor));
588                                        }
589                                }                       
590                        }
591                } catch (GitLabApiException e) {
592                        progressMonitor.worked(Status.ERROR, 1, "Failed to load branches", project, e);
593                }
594                
595                try {
596                        List<Contributor> contributors = repoApi.getContributors(project.getId());
597                        if (contributors != null) {
598                                EList<org.nasdanika.models.gitlab.Contributor> modelContributors = modelProject.getContributors();
599                                for (Contributor contributor: contributors) {
600                                        org.nasdanika.models.gitlab.Contributor modelContributor = createContributor(contributor, progressMonitor);
601                                        populateAbstractUser(contributor, modelContributor);
602                                        modelContributor.setAdditions(contributor.getAdditions());
603                                        modelContributor.setCommits(contributor.getCommits());
604                                        modelContributor.setDeletions(contributor.getDeletions());
605                                        modelContributors.add(modelContributor);
606                                }                       
607                        }
608                } catch (GitLabApiException e) {
609                        progressMonitor.worked(Status.ERROR, 1, "Failed to load contributors", project, e);
610                }
611                
612                try {
613                        ProjectApi projectApi = gitLabApi.getProjectApi();
614                        List<Member> projectMembers = projectApi.getMembers(project.getId());                   
615                        EList<org.nasdanika.models.gitlab.Member> modelProjectMembers = modelProject.getMembers();
616                        for (org.gitlab4j.api.models.Member member: projectMembers) {
617                                org.nasdanika.models.gitlab.Member modelMember = loadMember(member, progressMonitor);
618                                modelProjectMembers.add(modelMember);
619                        }
620                } catch (GitLabApiException e) {
621                        progressMonitor.worked(Status.ERROR, 1, "Failed to load members", project, e);                  
622                }
623                                
624//              CommitsApi commitsApi = gitLabApi.getCommitsApi();
625//              for (Commit commit: commitsApi.getCommits(project.getId())) {
626//                      System.out.println("Commit: " + commit);
627//              }
628                        
629                return modelProject;
630        }       
631        
632        /**
633         * Loads branch tree
634         * @param project
635         * @param groupProvider
636         * @param projectProvider
637         * @param progressMonitor
638         * @return
639         * @throws GitLabApiException 
640         */
641        protected List<org.nasdanika.models.gitlab.TreeItem> loadTree(
642                        org.nasdanika.models.gitlab.Project modelProject,
643                        org.nasdanika.models.gitlab.Branch modelBranch,
644                        String path,
645                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider,
646                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider,                 
647                        ProgressMonitor progressMonitor) throws GitLabApiException {
648                
649                List<org.nasdanika.models.gitlab.TreeItem> ret = new ArrayList<>();
650                RepositoryApi repoApi = gitLabApi.getRepositoryApi();
651                for (TreeItem treeItem: repoApi.getTree(modelProject.getId(), path, modelBranch.getName())) {
652                        if (isLoadPath(modelProject, modelBranch, path)) {
653                                switch (treeItem.getType()) {
654                                case TREE:
655                                        org.nasdanika.models.gitlab.Tree subTree = createTree(modelProject, modelBranch, treeItem);
656                                        subTree.setId(treeItem.getId());
657                                        subTree.setName(treeItem.getName());
658                                        subTree.setPath(treeItem.getPath());
659                                        subTree.getTreeItems().addAll(
660                                                        loadTree(
661                                                                        modelProject, 
662                                                                        modelBranch, 
663                                                                        treeItem.getPath(), 
664                                                                        groupProvider, 
665                                                                        projectProvider, 
666                                                                        progressMonitor));
667                                        ret.add(subTree);
668                                        break;
669                                case BLOB:
670                                        org.nasdanika.models.gitlab.Blob blob = createBlob(
671                                                        modelProject, 
672                                                        modelBranch, 
673                                                        treeItem, 
674                                                        groupProvider, 
675                                                        projectProvider, 
676                                                        progressMonitor);
677                                        if (blob != null) {
678                                                blob.setId(treeItem.getId());
679                                                blob.setName(treeItem.getName());
680                                                blob.setPath(treeItem.getPath());
681                                                
682                                                ret.add(blob);
683                                        }
684                                        break;
685                                case COMMIT:
686                                        break;
687                                default:
688                                        break;
689                                
690                                }
691                        }
692                }
693                
694                return ret;
695        }
696        
697        /**
698         * Creates a model tree. Override to create specialized trees.
699         * @param modelProject
700         * @param modelBranch
701         * @param tree
702         * @return
703         */
704        protected org.nasdanika.models.gitlab.Tree createTree(
705                        org.nasdanika.models.gitlab.Project modelProject,
706                        org.nasdanika.models.gitlab.Branch modelBranch,
707                        TreeItem tree) {
708                
709                return getFactory().createTree();
710        }
711                
712        /**
713         * Returns true if a tree items at the specified path shall be loaded. This method returns false.
714         * Override to load items of interest.
715         * @param modelProject
716         * @param modelBranch
717         * @param path
718         * @return 
719         */
720        protected boolean isLoadPath(
721                        org.nasdanika.models.gitlab.Project modelProject,
722                        org.nasdanika.models.gitlab.Branch modelBranch,
723                        String path) {
724                return false;
725        }
726        
727        /**
728         * Creates and populates a model blob. This implementation returns RepositoryFile. Override to create specialized blobs, e.g. TextRepositoryFile.
729         * This method may return null or an instance of org.nasdanika.models.gitlab.Blob to avoid calling to repository file API. 
730         * @param modelProject
731         * @param modelBranch
732         * @param blob
733         * @return
734         * @throws GitLabApiException 
735         */
736        protected org.nasdanika.models.gitlab.Blob createBlob(
737                        org.nasdanika.models.gitlab.Project modelProject,
738                        org.nasdanika.models.gitlab.Branch modelBranch,
739                        TreeItem blob,
740                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider,
741                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider,                 
742                        ProgressMonitor progressMonitor) throws GitLabApiException {
743
744                RepositoryFileApi repoFileApi = gitLabApi.getRepositoryFileApi();
745                RepositoryFile repoFile = repoFileApi.getFile(modelProject.getId(), blob.getPath(), modelBranch.getName());
746                org.nasdanika.models.gitlab.RepositoryFile ret = createRepositoryFile(
747                                modelProject, 
748                                modelBranch, 
749                                blob, 
750                                repoFile, 
751                                groupProvider, 
752                                projectProvider, 
753                                progressMonitor);
754                
755                ret.setCommitId(ret.getCommitId());
756                ret.setLastCommitId(ret.getLastCommitId());
757                ret.setRef(ret.getRef());
758                ret.setSize(ret.getSize());
759                return ret;
760        }
761        
762        /**
763         * Creates and populates a model repository file. This implementation returns RepositoryFile. Override to create specialized blobs, e.g. TextRepositoryFile.
764         * @param modelProject
765         * @param modelBranch
766         * @param blob
767         * @param repositoryFile
768         * @return
769         */
770        protected org.nasdanika.models.gitlab.RepositoryFile createRepositoryFile(
771                        org.nasdanika.models.gitlab.Project modelProject,
772                        org.nasdanika.models.gitlab.Branch modelBranch,
773                        TreeItem blob,
774                        RepositoryFile repositoryFile,
775                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Group>> groupProvider,
776                        Function<Long, CompletableFuture<org.nasdanika.models.gitlab.Project>> projectProvider,                 
777                        ProgressMonitor progressMonitor) {
778
779                return getFactory().createRepositoryFile();
780        }
781        
782        /**
783         * 
784         * @param member
785         * @param userProvider Provides a user instance to reference by the member
786         * @param progressMonitor
787         * @return
788         */
789        protected org.nasdanika.models.gitlab.Member loadMember(org.gitlab4j.api.models.Member member, ProgressMonitor progressMonitor) {
790                org.nasdanika.models.gitlab.Member modelMember = getFactory().createMember();
791                populateAbstractUser(member, modelMember);
792                org.gitlab4j.api.models.AccessLevel accessLevel = member.getAccessLevel();
793                if (accessLevel != null) {
794                        modelMember.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(accessLevel.value));
795                }
796                modelMember.setExpiresAt(member.getExpiresAt());                
797                return modelMember;
798        }       
799        
800        @Override
801        public void close() {
802                if (gitLabApi != null) {
803                        gitLabApi.close();
804                }
805        }
806
807}