Drive.java
/*
* onedrive-java-sdk - A Java SDK to access OneDrive drives and files.
* Copyright © 2023-2025 Andy Miles (andy.miles@amilesend.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.amilesend.onedrive.resource.drive;
import com.amilesend.client.parse.strategy.GsonExclude;
import com.amilesend.onedrive.connection.OneDriveConnection;
import com.amilesend.onedrive.resource.activities.ItemActivity;
import com.amilesend.onedrive.resource.identity.IdentitySet;
import com.amilesend.onedrive.resource.item.BaseItem;
import com.amilesend.onedrive.resource.item.DriveItem;
import com.amilesend.onedrive.resource.item.DriveItemPage;
import com.amilesend.onedrive.resource.item.SpecialDriveItem;
import com.amilesend.onedrive.resource.item.type.SharePointIds;
import com.amilesend.onedrive.resource.item.type.SpecialFolder;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.Validate;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static com.amilesend.onedrive.parse.resource.parser.Parsers.DRIVE_ITEM_PAGE_PARSER;
import static com.amilesend.onedrive.parse.resource.parser.Parsers.DRIVE_ITEM_PARSER;
import static com.amilesend.onedrive.parse.resource.parser.Parsers.ITEM_ACTIVITY_LIST_PARSER;
import static com.amilesend.onedrive.parse.resource.parser.Parsers.newSpecialDriveItemParser;
import static com.amilesend.onedrive.resource.ResourceHelper.objectDefinedEquals;
/**
* Top-level object that represents a user's OneDrive or SharePoint document library.
* <p>
* <a href="https://learn.microsoft.com/en-us/onedrive/developer/rest-api/resources/drive">API Documentation.</a>
*/
@Getter
@SuperBuilder
@ToString(callSuper = true)
public class Drive extends BaseItem {
public static final String DRIVE_BASE_URL_PATH = "/drive/";
public static final String DRIVES_BASE_URL_PATH = "/drives/";
public static final String DRIVE_URL_PATH_SUFFIX = "/drive";
public static final String DRIVES_URL_PATH_SUFFIX = "/drives";
private static final String ACTIVITIES_URL_SUFFIX = "/activities";
private static final String ROOT_FOLDER_URL_PATH = DRIVE_BASE_URL_PATH + "root";
private static final String CHANGES_URL_PATH = ROOT_FOLDER_URL_PATH + "/delta";
private static final String SEARCH_URL_PATH = ROOT_FOLDER_URL_PATH + "/search";
private static final String SPECIAL_FOLDER_URL_PATH = DRIVE_BASE_URL_PATH + "special/";
private static final int MAX_QUERY_LENGTH = 1000;
/**
* The drive type descriptor. Valid types are:
* <ul>
* <li>{@literal personal} - Personal drive</li>
* <li>{@literal business} - Business drive</li>
* <li>{@literal documentLibrary} - Sharepoint document library</li>
* </ul>
*/
private final String driveType;
/** The user account that owns the drive. */
private final IdentitySet owner;
/** Drive storage space quota information. */
private final Quota quota;
/** Identifiers used for SharePoint. */
private final SharePointIds sharepointIds;
/** Indicates that this is a system-managed drive: Note: Either {@code null} or defined as empty. */
private final Object system;
@GsonExclude
private final OneDriveConnection connection;
/**
* Queries and fetches the activities associated with this {@code Drive}.
* <p>
* <a href="https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/activities_list">
* API Documentation</a>.
*
* @return the list of activities
*/
public List<ItemActivity> getActivities() {
return connection.execute(
connection.newRequestBuilder()
.url(getActivitiesUrl(getId()))
.build(),
ITEM_ACTIVITY_LIST_PARSER);
}
/**
* Fetches the root folder associated with this {@code Drive}.
* <p>
* <a href="https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_get">
* API Documentation</a>.
* @return the root folder drive item
*/
public DriveItem getRootFolder() {
return connection.execute(
connection.newRequestBuilder()
.url(connection.getBaseUrl() + ROOT_FOLDER_URL_PATH)
.build(),
DRIVE_ITEM_PARSER);
}
/**
* Fetches the list of changes associated with this {@code Drive}.
* <p>
* <a href="https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_delta">
* API Documentation</a>.
*
* @return the list of drive item changes
*/
public List<DriveItem> getChanges() {
final List<DriveItem> changes = new ArrayList<>();
DriveItemPage currentPage = null;
do {
currentPage = connection.execute(
connection.newRequestBuilder()
.url(getChangesUrl(currentPage))
.build(),
DRIVE_ITEM_PAGE_PARSER);
changes.addAll(currentPage.getValue());
} while (hasNextPage(currentPage));
return changes;
}
/**
* Search for items associated with this {@code Drive}.
* <p>
* <a href="https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_search">
* API Documentation</a>.
*
* @param query the search query
* @return the list of drive items associated with the query
*/
public List<DriveItem> search(final String query) {
Validate.notBlank(query, "query must not be blank");
Validate.isTrue(query.length() < MAX_QUERY_LENGTH,
"query length must be less than " + MAX_QUERY_LENGTH);
final String encodedQuery = URLEncoder.encode(query, StandardCharsets.UTF_8);
final List<DriveItem> results = new ArrayList<>();
DriveItemPage currentPage = null;
do {
currentPage = connection.execute(
connection.newRequestBuilder()
.url(getSearchUrl(currentPage, encodedQuery))
.build(),
DRIVE_ITEM_PAGE_PARSER);
results.addAll(currentPage.getValue());
} while (hasNextPage(currentPage));
return results;
}
/**
* Retrieves a special folder for the given {@link SpecialFolder.Type}.
*
* @param type the special folder type
* @return the special folder drive item
* @see SpecialFolder.Type
*/
public SpecialDriveItem getSpecialFolder(@NonNull final SpecialFolder.Type type) {
return connection.execute(
connection.newRequestBuilder()
.url(getSpecialFolderUrl(type))
.build(),
newSpecialDriveItemParser(type));
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (!super.equals(obj)) {
return false;
}
final Drive drive = (Drive) obj;
return Objects.equals(getDriveType(), drive.getDriveType())
&& Objects.equals(getOwner(), drive.getOwner())
&& Objects.equals(getQuota(), drive.getQuota())
&& Objects.equals(getSharepointIds(), drive.getSharepointIds())
&& objectDefinedEquals(getSystem(), drive.getSystem());
}
@Override
public int hashCode() {
return Objects.hash(
super.hashCode(),
getDriveType(),
getOwner(),
getQuota(),
getSharepointIds(),
Objects.nonNull(getSystem()));
}
private String getActivitiesUrl(final String driveId) {
return new StringBuilder(connection.getBaseUrl())
.append(DRIVE_BASE_URL_PATH)
.append(URLEncoder.encode(driveId, StandardCharsets.UTF_8))
.append(ACTIVITIES_URL_SUFFIX)
.toString();
}
private String getChangesUrl(final DriveItemPage page) {
return page == null ? connection.getBaseUrl() + CHANGES_URL_PATH : page.getNextLink();
}
private String getSearchUrl(final DriveItemPage page, final String searchQuery) {
return page == null
? new StringBuilder(connection.getBaseUrl())
.append(SEARCH_URL_PATH)
.append("(q='")
.append(URLEncoder.encode(searchQuery, StandardCharsets.UTF_8))
.append("')")
.toString()
: page.getNextLink();
}
private String getSpecialFolderUrl(final SpecialFolder.Type type) {
return connection.getBaseUrl() + SPECIAL_FOLDER_URL_PATH + type.getId();
}
}