FeatureDecision for Sharepoint and Renaming of namespaces (#19953)

* Adds FeatureDecision. Initial steps on SharePoint -> Sharepoint rename
* Update I18n keys to use sharepoint over share_point
* Adds handling of the drives on the file picker
* Upload Strategy handling plus FileLink Contract ammendment
This commit is contained in:
Marcello Rocha
2025-08-20 14:53:49 +02:00
committed by GitHub
parent 5e6cc80f41
commit e9daa1ea51
88 changed files with 204 additions and 190 deletions
+6 -6
View File
@@ -49,11 +49,11 @@ ONE_DRIVE_TEST_OAUTH_CLIENT_SECRET=
ONE_DRIVE_TEST_OAUTH_CLIENT_ACCESS_TOKEN=
ONE_DRIVE_TEST_OAUTH_CLIENT_REFRESH_TOKEN=
SHARE_POINT_TEST_HOST=
SHARE_POINT_TEST_TENANT_ID=
SHAREPOINT_TEST_HOST=
SHAREPOINT_TEST_TENANT_ID=
SHARE_POINT_TEST_OAUTH_CLIENT_ID=
SHARE_POINT_TEST_OAUTH_CLIENT_SECRET=
SHAREPOINT_TEST_OAUTH_CLIENT_ID=
SHAREPOINT_TEST_OAUTH_CLIENT_SECRET=
SHARE_POINT_TEST_OAUTH_CLIENT_ACCESS_TOKEN=
SHARE_POINT_TEST_OAUTH_CLIENT_REFRESH_TOKEN=
SHAREPOINT_TEST_OAUTH_CLIENT_ACCESS_TOKEN=
SHAREPOINT_TEST_OAUTH_CLIENT_REFRESH_TOKEN=
@@ -46,7 +46,7 @@ import { FileLinksResourceService } from 'core-app/core/state/file-links/file-li
import { isDirectory, storageLocaleString } from 'core-app/shared/components/storages/functions/storages.functions';
import { StorageFilesResourceService } from 'core-app/core/state/storage-files/storage-files.service';
import {
StorageFileListItem,
StorageFileListItem, StorageFileListItemCheckbox,
} from 'core-app/shared/components/storages/storage-file-list-item/storage-file-list-item';
import {
FilePickerBaseModalComponent,
@@ -192,13 +192,19 @@ export class FilePickerModalComponent extends FilePickerBaseModalComponent {
this.enterDirectoryCallback(file),
false,
this.tooltip(file),
{
selected: this.selection.has(file.id as string),
changeSelection: () => { this.changeSelection(file); },
},
this.hasCheckbox(file),
);
}
private hasCheckbox(file:IStorageFile):StorageFileListItemCheckbox|undefined {
if (file.mimeType !== 'application/x-op-drive') {
return {
selected: this.selection.has(file.id as string),
changeSelection: () => { this.changeSelection(file); }
};
} else { return; }
}
private isAlreadyLinked(file:IStorageFile):boolean {
const currentFileLinks = this.locals.fileLinks as IFileLink[];
const found = currentFileLinks.find((a) => a.originData.id === file.id);
@@ -33,10 +33,14 @@ import {
} from 'core-app/shared/components/storages/icons.mapping';
import { IHalResourceLink } from 'core-app/core/state/hal-resource';
import { IFileLinkOriginData } from 'core-app/core/state/file-links/file-link.model';
import { nextcloud, oneDrive } from 'core-app/shared/components/storages/storages-constants.const';
import { nextcloud, oneDrive, sharepoint } from 'core-app/shared/components/storages/storages-constants.const';
export function isDirectory(originData:IFileLinkOriginData):boolean {
return originData.mimeType === 'application/x-op-directory';
return originData.mimeType === 'application/x-op-directory' || isDrive(originData);
}
function isDrive(originData:IFileLinkOriginData):boolean {
return originData.mimeType === 'application/x-op-drive';
}
export function getIconForMimeType(mimeType?:string):IFileIcon {
@@ -67,6 +71,7 @@ export function makeFilesCollectionLink(storageLink:IHalResourceLink, location:s
const storageTypeMap:Record<string, string> = {
[nextcloud]: 'js.storages.types.nextcloud',
[oneDrive]: 'js.storages.types.one_drive',
[sharepoint]: 'js.storages.types.sharepoint',
default: 'js.storages.types.default',
};
@@ -29,7 +29,7 @@
import { nextcloud } from 'core-app/shared/components/storages/storages-constants.const';
export interface IFileIcon {
icon:'image1'|'movie'|'file-text'|'export-pdf-descr'|'file-doc'|'file-sheet'|'file-presentation'|'folder'|'ticket'
icon:'image1'|'movie'|'file-text'|'export-pdf-descr'|'file-doc'|'file-sheet'|'file-presentation'|'folder'|'ticket'|'inbox'
clazz:'pdf'|'img'|'txt'|'doc'|'sheet'|'presentation'|'form'|'primary'|'mov'|'default'
}
@@ -100,6 +100,7 @@ export const fileIconMappings:Record<string, IFileIcon> = {
'video/3gpp-2': { icon: 'movie', clazz: 'mov' },
'application/x-op-directory': { icon: 'folder', clazz: 'primary' },
'application/x-op-drive': { icon: 'inbox', clazz: 'primary' },
default: { icon: 'ticket', clazz: 'default' },
};
@@ -31,7 +31,7 @@ import { TimezoneService } from 'core-app/core/datetime/timezone.service';
import { IFileIcon } from 'core-app/shared/components/storages/icons.mapping';
import { getIconForMimeType, isDirectory } from 'core-app/shared/components/storages/functions/storages.functions';
interface StorageFileListItemCheckbox {
export interface StorageFileListItemCheckbox {
selected:boolean;
changeSelection?:() => void;
}
@@ -1,6 +1,7 @@
// Storage types
export const nextcloud = 'urn:openproject-org:api:v3:storages:Nextcloud';
export const oneDrive = 'urn:openproject-org:api:v3:storages:OneDrive';
export const sharepoint = 'urn:openproject-org:api:v3:storages:Sharepoint';
// Storage authorization state
export const storageConnected = 'urn:openproject-org:api:v3:storages:authorization:Connected';
@@ -34,7 +34,7 @@ import { HttpClient, HttpEvent } from '@angular/common/http';
import { IUploadFile, OpUploadService } from 'core-app/core/upload/upload.service';
import { IUploadStrategy } from 'core-app/shared/components/storages/upload/upload-strategy';
import { NextcloudUploadStrategy } from 'core-app/shared/components/storages/upload/nextcloud-upload.strategy';
import { nextcloud, oneDrive } from 'core-app/shared/components/storages/storages-constants.const';
import { nextcloud, oneDrive, sharepoint } from 'core-app/shared/components/storages/storages-constants.const';
import { OneDriveUploadStrategy } from 'core-app/shared/components/storages/upload/one-drive-upload.strategy';
export interface IStorageFileUploadResponse {
@@ -73,6 +73,9 @@ export class StorageUploadService extends OpUploadService {
case oneDrive:
this.uploadStrategy = new OneDriveUploadStrategy(this.http);
break;
case sharepoint:
this.uploadStrategy = new OneDriveUploadStrategy(this.http);
break;
default:
throw new Error('unknown storage type');
}
@@ -31,12 +31,12 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
class Base
include TaggedLogging
include Dry::Monads::Result(Results::Error)
# @param storage [Storages::SharePointStorage]
# @param storage [Storages::SharepointStorage]
# @param auth_strategy [Result(Input::Strategy)]
# @param input_data [Data]
# @return [Success, Failure(Results::Error)]
@@ -44,7 +44,7 @@ module Storages
new(storage).call(auth_strategy:, input_data:)
end
# @param storage [Storages::SharePointStorage]
# @param storage [Storages::SharepointStorage]
def initialize(storage)
@storage = storage
end
@@ -66,7 +66,7 @@ module Storages
end
def split_identifier(location)
drive_id, item_id = location.to_s.split(SharePointStorage::IDENTIFIER_SEPARATOR)
drive_id, item_id = location.to_s.split(SharepointStorage::IDENTIFIER_SEPARATOR)
{ drive_id:, location: Peripherals::ParentFolder.new(item_id || "/") }
end
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Commands
class CreateFolderCommand < Base
def call(auth_strategy:, input_data:)
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Commands
class DeleteFolderCommand < Base
def call(auth_strategy:, input_data:)
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
OAuthConfiguration = Providers::OneDrive::OAuthConfiguration
end
end
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
class DownloadLinkQuery < Base
def call(auth_strategy:, input_data:)
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
class FileInfoQuery < Base
FIELDS = %w[id name fileSystemInfo file folder size createdBy lastModifiedBy parentReference webUrl].freeze
@@ -70,7 +70,7 @@ module Storages
end
end
def userless_strategy = Registry.resolve("share_point.authentication.userless").call
def userless_strategy = Registry.resolve("#{@storage}.authentication.userless").call
def storage_file_info(json, status: "ok", status_code: 200)
StorageFileTransformer.new(site_name).transform_file_info({ status:, status_code: }.merge(json))
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
class FilesInfoQuery < Base
def call(auth_strategy:, input_data:)
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
class FilesQuery < Base
def call(auth_strategy:, input_data:)
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
module Internal
class ChildrenQuery < Base
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
module Internal
class DriveItemQuery < Base
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
module Internal
class ListsQuery < Base
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
class OpenFileLinkQuery < Base
def call(auth_strategy:, input_data:)
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
class OpenStorageQuery < Base
def call(**)
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
class UploadLinkQuery < Base
def call(auth_strategy:, input_data:)
@@ -31,8 +31,8 @@
module Storages
module Adapters
module Providers
module SharePoint
class SharePointContract < ::ModelContract
module Sharepoint
class SharepointContract < ::ModelContract
attribute :name
validates :name, presence: true, length: { maximum: 255 }
@@ -31,8 +31,8 @@
module Storages
module Adapters
module Providers
module SharePoint
SharePointRegistry = Dry::Container::Namespace.new("share_point") do
module Sharepoint
SharepointRegistry = Dry::Container::Namespace.new("sharepoint") do
namespace("authentication") do
register(:userless, ->(use_cache = true) { Input::Strategy.build(key: :oauth_client_credentials, use_cache:) })
register(:user_bound, ->(user, storage = nil) { Input::Strategy.build(key: :oauth_user_token, user:, storage:) })
@@ -60,8 +60,8 @@ module Storages
end
namespace("contracts") do
register(:storage, SharePointContract)
register(:general_information, SharePointContract)
register(:storage, SharepointContract)
register(:general_information, SharepointContract)
end
namespace("validators") do
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
class StorageFileTransformer
attr_reader :site_name
@@ -112,13 +112,13 @@ module Storages
end
def compose_id(json)
"#{json.dig(:parentReference, :driveId)}#{SharePointStorage::IDENTIFIER_SEPARATOR}#{json[:id]}"
"#{json.dig(:parentReference, :driveId)}#{SharepointStorage::IDENTIFIER_SEPARATOR}#{json[:id]}"
end
def compose_parent_id(parent)
item_id = parent[:path].ends_with?("root:") ? nil : parent[:id]
"#{parent[:driveId]}#{SharePointStorage::IDENTIFIER_SEPARATOR}#{item_id}"
"#{parent[:driveId]}#{SharepointStorage::IDENTIFIER_SEPARATOR}#{item_id}"
end
end
end
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
class StorageWizard < Wizard
step :general_information, completed_if: ->(storage) { storage.name.present? }
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Validators
class AuthenticationValidator < ConnectionValidators::BaseValidatorGroup
def self.key = :authentication
@@ -58,13 +58,13 @@ module Storages
end
def user_bound_request
Registry["share_point.queries.user"].call(storage: @storage, auth_strategy:).either(
Registry["sharepoint.queries.user"].call(storage: @storage, auth_strategy:).either(
->(_) { pass_check(:user_bound_request) },
-> { fail_check(:user_bound_request, :"sp_oauth_request_#{it.code}") }
)
end
def auth_strategy = Registry["share_point.authentication.user_bound"].call(@user, @storage)
def auth_strategy = Registry["sharepoint.authentication.user_bound"].call(@user, @storage)
end
end
end
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Validators
class ConnectionValidator < ConnectionValidators::BaseConnectionValidator
register_group StorageConfigurationValidator
@@ -31,7 +31,7 @@
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Validators
class StorageConfigurationValidator < ConnectionValidators::BaseValidatorGroup
def self.key = :base_configuration
@@ -121,7 +121,7 @@ module Storages
end
end
def auth_strategy = Registry["share_point.authentication.userless"].call
def auth_strategy = Registry["sharepoint.authentication.userless"].call
def error_payload
@error_payload ||= query_result.either(->(_) { {} }, -> { MultiJson.load(it.payload, symbolize_keys: true) })
@@ -62,7 +62,7 @@ module Storages
# Need to make this dynamic to ease new providers to be registered
import Providers::Nextcloud::NextcloudRegistry
import Providers::OneDrive::OneDriveRegistry
import Providers::SharePoint::SharePointRegistry
import Providers::Sharepoint::SharepointRegistry
end
end
end
@@ -36,7 +36,6 @@ class Storages::FileLinks::CreateContract < ModelContract
attribute :container_type
attribute :origin_id
validates :origin_id, length: 1..100
attribute :origin_name
validates :origin_name, presence: true
attribute :origin_created_by_name
@@ -29,8 +29,8 @@
#++
module Storages
class SharePointStorage < Storage
IDENTIFIER_SEPARATOR = "||"
class SharepointStorage < Storage
IDENTIFIER_SEPARATOR = ":"
PROVIDER_FIELDS_DEFAULTS = {
automatically_managed: false,
@@ -42,10 +42,10 @@ module Storages
# For now SharePoint is visible only in tests.
# This is to prevent it from being shown in the UI, as it is not ready yet.
def self.visible?
Rails.env.local?
OpenProject::FeatureDecisions.sharepoint_storage_active? || Rails.env.local?
end
def self.short_provider_name = :share_point
def self.short_provider_name = :sharepoint
def self.non_confidential_provider_fields
super + %i[tenant_id]
@@ -69,7 +69,12 @@ module Storages
@uri ||= URI("https://graph.microsoft.com").normalize
end
def oauth_configuration = Adapters::Providers::SharePoint::OAuthConfiguration.new(self)
def connect_src
host_uri = URI(host)
["#{host_uri.scheme}://#{host_uri.host}"]
end
def oauth_configuration = Adapters::Providers::Sharepoint::OAuthConfiguration.new(self)
def provider_fields_defaults
PROVIDER_FIELDS_DEFAULTS
@@ -199,7 +199,7 @@ module Storages
end
def provider_type_share_point?
is_a?(SharePointStorage)
is_a?(SharepointStorage)
end
def health_reason_identifier
+5 -5
View File
@@ -22,7 +22,7 @@ en:
project_folder_mode: Project folder mode
storage: Storage
storage_url: Storage URL
storages/share_point_storage:
storages/sharepoint_storage:
host: Sharepoint Site URL
library: Library ID
name: Name
@@ -196,10 +196,10 @@ en:
oauth_client_incomplete:
nextcloud: Allow OpenProject to access Nextcloud data using OAuth.
one_drive: Allow OpenProject to access Azure data using OAuth to connect OneDrive.
share_point: Allow OpenProject to access SharePoint data using OAuth.
sharepoint: Allow OpenProject to access SharePoint data using OAuth.
redirect_uri_incomplete:
one_drive: Complete the setup with the correct URI redirection.
share_point: Complete the setup with the correct URI redirection.
sharepoint: Complete the setup with the correct URI redirection.
confirm_replace_oauth_application: This action will reset the current OAuth credentials. After confirming you will have to reenter the credentials at the storage provider and all remote users will have to authorize against OpenProject again. Are you sure you want to proceed?
confirm_replace_oauth_client: This action will reset the current OAuth credentials. After confirming you will have to enter new credentials from the storage provider and all users will have to authorize against %{provider_type} again. Are you sure you want to proceed?
delete_warning:
@@ -396,7 +396,7 @@ en:
setting_up_additional_storages_non_admin: Administrators can set up additional file storages in Administration / File storages.
setting_up_storages: For setting up file storages, please visit
setting_up_storages_non_admin: Administrators can set up file storages in Administration / File storages.
share_point:
sharepoint:
application_link_text: SharePoint site
integration: SharePoint Administration / OpenProject
oauth_configuration: Copy these values from the desired application in the %{application_link_text}.
@@ -502,7 +502,7 @@ en:
label_oauth_client_secret: Azure OAuth Client Secret Value
name: OneDrive
name_placeholder: e.g. OneDrive
share_point:
sharepoint:
label_oauth_client_id: Azure OAuth Application (client) ID
label_oauth_client_secret: Azure OAuth Client Secret Value
name: SharePoint
+2 -1
View File
@@ -18,7 +18,8 @@ en:
types:
nextcloud: "Nextcloud"
one_drive: "OneDrive/SharePoint"
one_drive: "OneDrive"
sharepoint: "SharePoint"
default: "Storage"
information:
@@ -40,12 +40,12 @@ module API::V3::Storages
URN_STORAGE_TYPE_NEXTCLOUD = "#{::API::V3::URN_PREFIX}storages:Nextcloud".freeze
URN_STORAGE_TYPE_ONE_DRIVE = "#{::API::V3::URN_PREFIX}storages:OneDrive".freeze
URN_STORAGE_TYPE_SHARE_POINT = "#{::API::V3::URN_PREFIX}storages:SharePoint".freeze
URN_STORAGE_TYPE_SHARE_POINT = "#{::API::V3::URN_PREFIX}storages:Sharepoint".freeze
STORAGE_TYPE_MAP = {
URN_STORAGE_TYPE_NEXTCLOUD => Storages::NextcloudStorage.name,
URN_STORAGE_TYPE_ONE_DRIVE => Storages::OneDriveStorage.name,
URN_STORAGE_TYPE_SHARE_POINT => Storages::SharePointStorage.name
URN_STORAGE_TYPE_SHARE_POINT => Storages::SharepointStorage.name
}.freeze
STORAGE_TYPE_URN_MAP = STORAGE_TYPE_MAP.invert.freeze
@@ -52,6 +52,7 @@ module OpenProject::Storages
initializer "openproject_storages.feature_decisions" do
OpenProject::FeatureDecisions.add :storage_file_picking_select_all
OpenProject::FeatureDecisions.add :sharepoint_storage
end
initializer "openproject_storages.event_subscriptions" do
@@ -261,7 +262,7 @@ module OpenProject::Storages
Storages::Storage::InexistentStorage
Storages::OneDriveStorage
Storages::NextcloudStorage
Storages::SharePointStorage
Storages::SharepointStorage
# Allow the browser to connect to external servers for direct file uploads.
AppendStoragesHostsToCspHook
@@ -34,17 +34,17 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Commands
RSpec.describe CreateFolderCommand, :webmock do
let(:storage) { create(:share_point_storage, :sandbox) }
let(:auth_strategy) { Registry.resolve("share_point.authentication.userless").call(false) }
let(:storage) { create(:sharepoint_storage, :sandbox) }
let(:auth_strategy) { Registry.resolve("sharepoint.authentication.userless").call(false) }
let(:base_drive) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW" }
let(:input_data) { Input::CreateFolder.build(folder_name:, parent_location:).value! }
it_behaves_like "adapter create_folder_command: basic command setup"
context "when creating a folder in the root", vcr: "share_point/create_folder_root" do
context "when creating a folder in the root", vcr: "sharepoint/create_folder_root" do
let(:folder_name) { "Földer CreatedBy Çommand" }
let(:parent_location) { composite_identifier(nil) }
let(:path) { "/Marcello%20VCR/F%C3%B6lder%20CreatedBy%20%C3%87ommand" }
@@ -52,7 +52,7 @@ module Storages
it_behaves_like "adapter create_folder_command: successful folder creation"
end
context "when creating a folder in a parent folder", vcr: "share_point/create_folder_parent" do
context "when creating a folder in a parent folder", vcr: "sharepoint/create_folder_parent" do
let(:folder_name) { "Földer CreatedBy Çommand" }
let(:parent_location) { composite_identifier("01ANJ53W7TITEF4WCHRBDKR7VMNUWZ33WD") }
let(:path) { "/Marcello%20VCR/Folder%20with%20spaces/F%C3%B6lder%20CreatedBy%20%C3%87ommand" }
@@ -60,14 +60,14 @@ module Storages
it_behaves_like "adapter create_folder_command: successful folder creation"
end
context "when creating a folder in a non-existing parent folder", vcr: "share_point/create_folder_parent_not_found" do
context "when creating a folder in a non-existing parent folder", vcr: "sharepoint/create_folder_parent_not_found" do
let(:folder_name) { "Földer CreatedBy Çommand" }
let(:parent_location) { composite_identifier("01AZJL5PKU2WV3U3RKKFF4A7ZCWVBXRTEU") }
it_behaves_like "adapter create_folder_command: parent not found"
end
context "when folder already exists", vcr: "share_point/create_folder_already_exists" do
context "when folder already exists", vcr: "sharepoint/create_folder_already_exists" do
let(:folder_name) { "data" }
let(:parent_location) { composite_identifier(nil) }
@@ -75,7 +75,7 @@ module Storages
end
context "when trying to create a folder under the root of the site",
vcr: "share_point/create_folder_invalid_request" do
vcr: "sharepoint/create_folder_invalid_request" do
let(:folder_name) { "cant_do_that_folder" }
let(:parent_location) { "/" }
@@ -92,11 +92,11 @@ module Storages
private
def composite_identifier(item_id) = "#{base_drive}#{SharePointStorage::IDENTIFIER_SEPARATOR}#{item_id}"
def composite_identifier(item_id) = "#{base_drive}#{SharepointStorage::IDENTIFIER_SEPARATOR}#{item_id}"
def delete_created_folder(folder)
Input::DeleteFolder.build(location: folder.id).bind do |input_data|
Registry.resolve("share_point.commands.delete_folder").call(storage:, auth_strategy:, input_data:)
Registry.resolve("sharepoint.commands.delete_folder").call(storage:, auth_strategy:, input_data:)
end
end
end
@@ -34,15 +34,15 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Commands
RSpec.describe DeleteFolderCommand, :vcr, :webmock do
let(:storage) { create(:share_point_storage, :sandbox) }
let(:auth_strategy) { Registry["share_point.authentication.userless"].call }
let(:storage) { create(:sharepoint_storage, :sandbox) }
let(:auth_strategy) { Registry["sharepoint.authentication.userless"].call }
let(:base_drive) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW" }
it "is registered as commands.share_point.delete_folder" do
expect(Registry.resolve("share_point.commands.delete_folder")).to eq(described_class)
it "is registered as commands.sharepoint.delete_folder" do
expect(Registry.resolve("sharepoint.commands.delete_folder")).to eq(described_class)
end
it ".call requires storage and input_data as keyword arguments" do
@@ -52,11 +52,11 @@ module Storages
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
it "deletes a folder", vcr: "share_point/delete_folder" do
it "deletes a folder", vcr: "sharepoint/delete_folder" do
create_result = Input::CreateFolder
.build(folder_name: "To Be Deleted Soon", parent_location: composite_identifier(nil))
.bind do |input_data|
Registry.resolve("share_point.commands.create_folder").call(storage:, auth_strategy:, input_data:)
Registry.resolve("sharepoint.commands.create_folder").call(storage:, auth_strategy:, input_data:)
end
folder = create_result.value_or { fail("Folder Creation Failed") }
@@ -66,7 +66,7 @@ module Storages
end
end
it "when the folder is not found, returns a failure", vcr: "share_point/delete_folder_not_found" do
it "when the folder is not found, returns a failure", vcr: "sharepoint/delete_folder_not_found" do
result = Input::DeleteFolder.build(location: composite_identifier("NOT_HERE")).bind do |input_data|
described_class.call(storage:, auth_strategy:, input_data:)
end
@@ -77,7 +77,7 @@ module Storages
private
def composite_identifier(item_id) = "#{base_drive}#{SharePointStorage::IDENTIFIER_SEPARATOR}#{item_id}"
def composite_identifier(item_id) = "#{base_drive}#{SharepointStorage::IDENTIFIER_SEPARATOR}#{item_id}"
end
end
end
@@ -34,17 +34,17 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
RSpec.describe DownloadLinkQuery, :vcr, :webmock do
let(:user) { create(:user) }
let(:storage) { create(:share_point_storage, :sandbox, oauth_client_token_user: user) }
let(:storage) { create(:sharepoint_storage, :sandbox, oauth_client_token_user: user) }
let(:auth_strategy) { Registry["one_drive.authentication.user_bound"].call(user, storage) }
let(:drive_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY87vnZ6fgfvQanZHX-XCAyw" }
let(:file_link) { create(:file_link, origin_id: "#{drive_id}||01ANJ53W4ELLSQL3JZHNA2MHKKHKAUQWNS") }
let(:not_existent_file_link) { create(:file_link, origin_id: "#{drive_id}||IHaveTheHighGround") }
let(:folder_file_link) { create(:file_link, origin_id: "#{drive_id}||01ANJ53W4MWZNYS4FOTNDI3UVTLEXUZ5TQ") }
let(:file_link) { create(:file_link, origin_id: "#{drive_id}:01ANJ53W4ELLSQL3JZHNA2MHKKHKAUQWNS") }
let(:not_existent_file_link) { create(:file_link, origin_id: "#{drive_id}:IHaveTheHighGround") }
let(:folder_file_link) { create(:file_link, origin_id: "#{drive_id}:01ANJ53W4MWZNYS4FOTNDI3UVTLEXUZ5TQ") }
let(:input_data) { Input::DownloadLink.build(file_link:).value! }
@@ -59,7 +59,7 @@ module Storages
end
context "with outbound request successful" do
it "returns a result with a download url", vcr: "share_point/download_link_query_success" do
it "returns a result with a download url", vcr: "sharepoint/download_link_query_success" do
download_link = subject.call(auth_strategy:, input_data:)
expect(download_link).to be_success
@@ -69,7 +69,7 @@ module Storages
expect(uri.path).to eq("/sites/OPTest/_layouts/15/download.aspx")
end
it "returns an error on folders", vcr: "share_point/download_link_query_folder" do
it "returns an error on folders", vcr: "sharepoint/download_link_query_folder" do
input_data = Input::DownloadLink.build(file_link: folder_file_link).value!
download_link = subject.call(auth_strategy:, input_data:)
@@ -81,7 +81,7 @@ module Storages
expect(error.code).to eq(:not_found)
end
it "returns an error if the file is not found", vcr: "share_point/download_link_query_not_found" do
it "returns an error if the file is not found", vcr: "sharepoint/download_link_query_not_found" do
input_data = Input::DownloadLink.build(file_link: not_existent_file_link).value!
download_link = subject.call(auth_strategy:, input_data:)
@@ -34,21 +34,21 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
RSpec.describe FileInfoQuery, :webmock do
let(:user) { create(:user) }
let(:storage) { create(:share_point_storage, :sandbox, oauth_client_token_user: user) }
let(:storage) { create(:sharepoint_storage, :sandbox, oauth_client_token_user: user) }
let(:drive_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY87vnZ6fgfvQanZHX-XCAyw" }
let(:auth_strategy) { Registry["share_point.authentication.user_bound"].call(user, storage) }
let(:auth_strategy) { Registry["sharepoint.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::FileInfo.build(file_id:).value! }
it_behaves_like "adapter file_info_query: basic query setup"
context "with a file id requested", vcr: "share_point/file_info_query_success_file" do
let(:file_id) { "#{drive_id}||01ANJ53W5UJK2CQO6IY5HLBVYBVNJ4TKHZ" }
context "with a file id requested", vcr: "sharepoint/file_info_query_success_file" do
let(:file_id) { "#{drive_id}:01ANJ53W5UJK2CQO6IY5HLBVYBVNJ4TKHZ" }
let(:file_info) do
Results::StorageFileInfo.new(
id: "01ANJ53W5UJK2CQO6IY5HLBVYBVNJ4TKHZ",
@@ -71,8 +71,8 @@ module Storages
it_behaves_like "adapter file_info_query: successful file/folder response"
end
context "with a folder id requested", vcr: "share_point/file_info_query_success_folder" do
let(:file_id) { "#{drive_id}||01ANJ53WYP6TBC6T4G2RHIU4SVNEYGL6MF" }
context "with a folder id requested", vcr: "sharepoint/file_info_query_success_folder" do
let(:file_id) { "#{drive_id}:01ANJ53WYP6TBC6T4G2RHIU4SVNEYGL6MF" }
let(:file_info) do
Results::StorageFileInfo.new(
id: "01ANJ53WYP6TBC6T4G2RHIU4SVNEYGL6MF",
@@ -96,8 +96,8 @@ module Storages
end
context "with a file with special characters in the path",
vcr: "share_point/file_info_query_success_special_characters" do
let(:file_id) { "#{drive_id}||01ANJ53W7BT4LBZ3PNORCYAXKILWJBLEBV" }
vcr: "sharepoint/file_info_query_success_special_characters" do
let(:file_id) { "#{drive_id}:01ANJ53W7BT4LBZ3PNORCYAXKILWJBLEBV" }
let(:file_info) do
Results::StorageFileInfo.new(
id: "01ANJ53W7BT4LBZ3PNORCYAXKILWJBLEBV",
@@ -120,8 +120,8 @@ module Storages
it_behaves_like "adapter file_info_query: successful file/folder response"
end
context "with a not existing file id", vcr: "share_point/file_info_query_not_found" do
let(:file_id) { "#{drive_id}||not_existent" }
context "with a not existing file id", vcr: "sharepoint/file_info_query_not_found" do
let(:file_id) { "#{drive_id}:not_existent" }
let(:error_source) { Internal::DriveItemQuery }
it_behaves_like "adapter file_info_query: not found"
@@ -34,12 +34,12 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
RSpec.describe FilesInfoQuery, :vcr, :webmock do
let(:user) { create(:user) }
let(:storage) { create(:share_point_storage, :sandbox, oauth_client_token_user: user) }
let(:auth_strategy) { Registry["share_point.authentication.user_bound"].call(user, storage) }
let(:storage) { create(:sharepoint_storage, :sandbox, oauth_client_token_user: user) }
let(:auth_strategy) { Registry["sharepoint.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::FilesInfo.build(file_ids:).value! }
it_behaves_like "adapter files_info_query: basic query setup"
@@ -51,13 +51,13 @@ module Storages
it_behaves_like "adapter files_info_query: successful list response"
end
context "with all outbound requests successful", vcr: "share_point/files_info_query_success" do
context "with all outbound requests successful", vcr: "sharepoint/files_info_query_success" do
let(:drive_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY87vnZ6fgfvQanZHX-XCAyw" }
let(:file_ids) do
%W[
#{drive_id}||01ANJ53WYLXAJW5PXSCJB2CFCD42UPDKMI
#{drive_id}||01ANJ53W4ELLSQL3JZHNA2MHKKHKAUQWNS
#{drive_id}||01ANJ53W5UJK2CQO6IY5HLBVYBVNJ4TKHZ
#{drive_id}:01ANJ53WYLXAJW5PXSCJB2CFCD42UPDKMI
#{drive_id}:01ANJ53W4ELLSQL3JZHNA2MHKKHKAUQWNS
#{drive_id}:01ANJ53W5UJK2CQO6IY5HLBVYBVNJ4TKHZ
]
end
let(:expected_file_infos) do
@@ -113,9 +113,9 @@ module Storages
it_behaves_like "adapter files_info_query: successful list response"
end
context "with one outbound request returning not found", vcr: "share_point/files_info_query_one_not_found" do
context "with one outbound request returning not found", vcr: "sharepoint/files_info_query_one_not_found" do
let(:drive_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY87vnZ6fgfvQanZHX-XCAyw" }
let(:file_ids) { %W[#{drive_id}||01ANJ53W4ELLSQL3JZHNA2MHKKHKAUQWNS #{drive_id}||not_existent] }
let(:file_ids) { %W[#{drive_id}:01ANJ53W4ELLSQL3JZHNA2MHKKHKAUQWNS #{drive_id}:not_existent] }
let(:expected_file_infos) do
[
Results::StorageFileInfo.new(
@@ -34,19 +34,18 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
RSpec.describe FilesQuery, :webmock do
let(:user) { create(:admin) }
let(:storage) { create(:share_point_storage, :sandbox, oauth_client_token_user: user) }
let(:storage) { create(:sharepoint_storage, :sandbox, oauth_client_token_user: user) }
let(:auth_strategy) { Registry["share_point.authentication.userless"].call(false) }
let(:auth_strategy) { Registry["sharepoint.authentication.userless"].call(false) }
let(:input_data) { Input::Files.build(folder:).value! }
it_behaves_like "adapter files_query: basic query setup"
# rubocop:disable Layout/LineLength
context "when parent folder is root", vcr: "share_point/files_query_root" do
context "when parent folder is root", vcr: "sharepoint/files_query_root" do
let(:folder) { "/" }
let(:files_result) do
Results::StorageFileCollection.new(
@@ -119,13 +118,13 @@ module Storages
it_behaves_like "adapter files_query: successful files response"
end
context "when requesting a drive", vcr: "share_point/files_query_drive" do
context "when requesting a drive", vcr: "sharepoint/files_query_drive" do
let(:folder) { "/Marcello VCR" }
let(:files_result) do
Results::StorageFileCollection.new(
files: [
Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S",
name: "data",
size: 12605,
mime_type: "application/x-op-directory",
@@ -137,7 +136,7 @@ module Storages
permissions: %i[readable writeable]
),
Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W2MWJ6SKEZPHFGIAAB325KYYMPE",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W2MWJ6SKEZPHFGIAAB325KYYMPE",
name: "empty",
size: 0,
mime_type: "application/x-op-directory",
@@ -149,7 +148,7 @@ module Storages
permissions: %i[readable writeable]
),
Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W7TITEF4WCHRBDKR7VMNUWZ33WD",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W7TITEF4WCHRBDKR7VMNUWZ33WD",
name: "Folder with spaces",
size: 0,
mime_type: "application/x-op-directory",
@@ -161,7 +160,7 @@ module Storages
permissions: %i[readable writeable]
),
Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53WZVLAWJSVFKOFF3HLYZPMPUK6HI",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53WZVLAWJSVFKOFF3HLYZPMPUK6HI",
name: "simply_oidc.jpg",
size: 56483,
mime_type: "image/jpeg",
@@ -174,7 +173,7 @@ module Storages
)
],
parent: Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:",
name: "Marcello VCR",
location: "/Marcello%20VCR",
permissions: %i[readable writeable]
@@ -188,14 +187,14 @@ module Storages
it_behaves_like "adapter files_query: successful files response"
end
context "when requesting an folder", vcr: "share_point/files_query_folder" do
context "when requesting an folder", vcr: "sharepoint/files_query_folder" do
let(:folder) { "/Marcello VCR/data" }
let(:files_result) do
Results::StorageFileCollection.new(
files: [
Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W6DBDYX553L4REYNOMUI6XVMTO6",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W6DBDYX553L4REYNOMUI6XVMTO6",
name: "subfolder",
size: 11845,
mime_type: "application/x-op-directory",
@@ -207,7 +206,7 @@ module Storages
permissions: %i[readable writeable]
),
Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W26P5RNXU7V2JBKCVQQAGGTO46A",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W26P5RNXU7V2JBKCVQQAGGTO46A",
name: "edge one_drive_health_report_2025-07-22T16_03_25Z.txt",
size: 760,
mime_type: "text/plain",
@@ -220,7 +219,7 @@ module Storages
)
],
parent: Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S",
name: "data",
location: "/Marcello%20VCR/data",
permissions: %i[readable writeable]
@@ -235,13 +234,13 @@ module Storages
it_behaves_like "adapter files_query: successful files response"
end
context "when requesting a sub folder", vcr: "share_point/files_query_sub_folder" do
context "when requesting a sub folder", vcr: "sharepoint/files_query_sub_folder" do
let(:folder) { "/Marcello VCR/data/subfolder" }
let(:files_result) do
Results::StorageFileCollection.new(
files: [
Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W7MUYDYQAA3WVEYDJQNZVSKNPGD",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W7MUYDYQAA3WVEYDJQNZVSKNPGD",
name: "fw13-easy-effects.json",
size: 11845,
mime_type: "application/json",
@@ -254,7 +253,7 @@ module Storages
)
],
parent: Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W6DBDYX553L4REYNOMUI6XVMTO6",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W6DBDYX553L4REYNOMUI6XVMTO6",
name: "subfolder",
location: "/Marcello%20VCR/data/subfolder",
permissions: %i[readable writeable]
@@ -270,14 +269,14 @@ module Storages
it_behaves_like "adapter files_query: successful files response"
end
context "when requesting an empty folder", vcr: "share_point/files_query_empty_folder" do
context "when requesting an empty folder", vcr: "sharepoint/files_query_empty_folder" do
let(:folder) { "/Marcello VCR/empty" }
let(:files_result) do
Results::StorageFileCollection.new(
files: [],
parent: Results::StorageFile.new(
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W2MWJ6SKEZPHFGIAAB325KYYMPE",
id: "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W2MWJ6SKEZPHFGIAAB325KYYMPE",
name: "empty",
location: "/Marcello%20VCR/empty",
permissions: %i[readable writeable]
@@ -292,7 +291,7 @@ module Storages
it_behaves_like "adapter files_query: successful files response"
end
context "when requesting an empty library", vcr: "share_point/files_query_empty_drive" do
context "when requesting an empty library", vcr: "sharepoint/files_query_empty_drive" do
let(:folder) { "/Selected Permissions" }
let(:files_result) do
@@ -313,18 +312,17 @@ module Storages
it_behaves_like "adapter files_query: successful files response"
end
context "when requesting an unknown file", vcr: "share_point/files_query_file_not_found" do
context "when requesting an unknown file", vcr: "sharepoint/files_query_file_not_found" do
let(:folder) { "/Marcello VCR/POTATO" }
it_behaves_like "adapter files_query: not found", Internal::ChildrenQuery
end
context "when requestion an unknown drive", vcr: "share_point/files_query_drive_not_found" do
context "when requestion an unknown drive", vcr: "sharepoint/files_query_drive_not_found" do
let(:folder) { "/That is no moon" }
it_behaves_like "adapter files_query: not found"
end
# rubocop:enable Layout/LineLength
end
end
end
@@ -34,19 +34,19 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
RSpec.describe OpenFileLinkQuery, :vcr, :webmock do
let(:user) { create(:user) }
let(:storage) { create(:share_point_storage, :sandbox, oauth_client_token_user: user) }
let(:auth_strategy) { Registry.resolve("share_point.authentication.user_bound").call(user, storage) }
let(:storage) { create(:sharepoint_storage, :sandbox, oauth_client_token_user: user) }
let(:auth_strategy) { Registry.resolve("sharepoint.authentication.user_bound").call(user, storage) }
let(:drive_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY87vnZ6fgfvQanZHX-XCAyw" }
let(:separator) { SharePointStorage::IDENTIFIER_SEPARATOR }
let(:separator) { SharepointStorage::IDENTIFIER_SEPARATOR }
it_behaves_like "adapter open_file_link_query: basic query setup"
context "with outbound requests successful" do
context "with open location flag not set", vcr: "share_point/open_file_link_query_success" do
context "with open location flag not set", vcr: "sharepoint/open_file_link_query_success" do
let(:file_id) { "#{drive_id}#{separator}01ANJ53WYLXAJW5PXSCJB2CFCD42UPDKMI" }
let(:input_data) { Input::OpenFileLink.build(file_id:).value! }
let(:open_file_link) { "https://ymt6d.sharepoint.com/sites/OPTest/Shared%20Documents/Folder" }
@@ -54,7 +54,7 @@ module Storages
it_behaves_like "adapter open_file_link_query: successful link response"
end
context "with open location flag set", vcr: "share_point/open_file_link_location_query_success" do
context "with open location flag set", vcr: "sharepoint/open_file_link_location_query_success" do
let(:file_id) { "#{drive_id}#{separator}01ANJ53WYLXAJW5PXSCJB2CFCD42UPDKMI" }
let(:input_data) { Input::OpenFileLink.build(file_id:, open_location: true).value! }
let(:open_file_link) { "https://ymt6d.sharepoint.com/sites/OPTest/Shared%20Documents" }
@@ -62,7 +62,7 @@ module Storages
it_behaves_like "adapter open_file_link_query: successful link response"
context "if file id already points at root element",
vcr: "share_point/open_file_link_location_on_root_query_success" do
vcr: "sharepoint/open_file_link_location_on_root_query_success" do
let(:file_id) { "#{drive_id}#{separator}01ANJ53W56Y2GOVW7725BZO354PWSELRRZ" }
it_behaves_like "adapter open_file_link_query: successful link response"
@@ -70,7 +70,7 @@ module Storages
end
end
context "with not existent file id", vcr: "share_point/open_file_link_query_not_found" do
context "with not existent file id", vcr: "sharepoint/open_file_link_query_not_found" do
let(:file_id) { "#{drive_id}#{separator}YouShallNotPass" }
let(:input_data) { Input::OpenFileLink.build(file_id:).value! }
let(:error_source) { Internal::DriveItemQuery }
@@ -34,10 +34,10 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
RSpec.describe OpenStorageQuery do
let(:storage) { create(:share_point_storage, :sandbox) }
let(:storage) { create(:sharepoint_storage, :sandbox) }
it "responds to .call" do
expect(described_class).to respond_to(:call)
@@ -34,18 +34,18 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Queries
RSpec.describe UploadLinkQuery, :webmock do
let(:storage) { create(:share_point_storage, :sandbox) }
let(:auth_strategy) { Registry["share_point.authentication.userless"].call }
let(:storage) { create(:sharepoint_storage, :sandbox) }
let(:auth_strategy) { Registry["sharepoint.authentication.userless"].call }
let(:upload_method) { :put }
let(:input_data) { Input::UploadLink.build(folder_id:, file_name:).value! }
let(:file_name) { "DeathStart_blueprints.tiff" }
it_behaves_like "adapter upload_link_query: basic query setup"
context "when creating an upload link to the root folder of a list", vcr: "share_point/upload_link_success" do
context "when creating an upload link to the root folder of a list", vcr: "sharepoint/upload_link_success" do
let(:folder_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8Qconfm2i6SKEoCmuGYqQK" }
let(:token) do
@@ -69,9 +69,9 @@ module Storages
it_behaves_like "adapter upload_link_query: successful upload link response"
end
context "when creating an upload link to a subfolder on a list", vcr: "share_point/upload_link_subfolder" do
context "when creating an upload link to a subfolder on a list", vcr: "sharepoint/upload_link_subfolder" do
let(:folder_id) do
"b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW||01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S"
"b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW:01ANJ53W5P3SUY3ZCDTRA3KLXRGA5A2M3S"
end
let(:token) do
"v1.eyJzaXRlaWQiOiIxMDk5ZTMxNS1kMGM3LTQ3YzctODY0MC1hYTk1MDRiNzBmZmYiLCJhcHBfZGlzcGxheW5hbWUiOiJPUCBTZWx" \
@@ -93,11 +93,11 @@ module Storages
it_behaves_like "adapter upload_link_query: successful upload link response"
end
context "when requesting an upload link for a not existing file", vcr: "share_point/upload_link_not_found" do
context "when requesting an upload link for a not existing file", vcr: "sharepoint/upload_link_not_found" do
let(:input_data) do
Input::UploadLink.build(
folder_id:
"b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8Qconfm2i6SKEoCmuGYqQK||04AZJL5PN6Y2GOVW7725BZO354PWSELRRZ",
"b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8Qconfm2i6SKEoCmuGYqQK:04AZJL5PN6Y2GOVW7725BZO354PWSELRRZ",
file_name: "DeathStart_blueprints.tiff"
).value!
end
@@ -34,10 +34,10 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
RSpec.describe SharePointContract, :storage_server_helpers, :webmock do
module Sharepoint
RSpec.describe SharepointContract, :storage_server_helpers, :webmock do
let(:current_user) { create(:admin) }
let(:storage) { build(:share_point_storage, :with_tenant_id) }
let(:storage) { build(:sharepoint_storage, :with_tenant_id) }
# As the SharePointContract is selected by the BaseContract to make writable attributes available,
# the BaseContract needs to be instantiated here.
@@ -30,10 +30,10 @@
require "spec_helper"
RSpec.describe Storages::Adapters::Providers::SharePoint::StorageWizard do
RSpec.describe Storages::Adapters::Providers::Sharepoint::StorageWizard do
subject(:wizard) { described_class.new(model:, user:) }
let(:model) { Storages::SharePointStorage.new }
let(:model) { Storages::SharepointStorage.new }
let(:user) { create(:admin) }
before do
@@ -3,12 +3,12 @@
require "spec_helper"
require_module_spec_helper
RSpec.describe Storages::Adapters::Providers::SharePoint::Validators::AuthenticationValidator, :webmock do
RSpec.describe Storages::Adapters::Providers::Sharepoint::Validators::AuthenticationValidator, :webmock do
subject(:validator) { described_class.new(storage) }
context "when using OAuth2" do
let(:user) { create(:user) }
let(:storage) { create(:share_point_storage, :sandbox, oauth_client_token_user: user) }
let(:storage) { create(:sharepoint_storage, :sandbox, oauth_client_token_user: user) }
let(:error) { Storages::Adapters::Results::Error.new(code: :unauthorized, source: self) }
before { User.current = user }
@@ -17,7 +17,7 @@ RSpec.describe Storages::Adapters::Providers::SharePoint::Validators::Authentica
expect(described_class.key).to eq(:authentication)
end
it "passes when the user has a token and the request works", vcr: "share_point/user_query_success" do
it "passes when the user has a token and the request works", vcr: "sharepoint/user_query_success" do
expect(validator.call).to be_success
end
@@ -31,7 +31,7 @@ RSpec.describe Storages::Adapters::Providers::SharePoint::Validators::Authentica
end
it "returns a failure if the remote call failed" do
Storages::Adapters::Registry.stub("share_point.queries.user", ->(_) { Failure(error) })
Storages::Adapters::Registry.stub("sharepoint.queries.user", ->(_) { Failure(error) })
result = validator.call
expect(result[:user_bound_request]).to be_a_failure
@@ -6,15 +6,15 @@ require_module_spec_helper
module Storages
module Adapters
module Providers
module SharePoint
module Sharepoint
module Validators
RSpec.describe StorageConfigurationValidator, :webmock do
let(:storage) { create(:share_point_storage, :sandbox, :as_automatically_managed) }
let(:storage) { create(:sharepoint_storage, :sandbox, :as_automatically_managed) }
let(:error) { Results::Error.new(code: error_code, source: self) }
subject(:validator) { described_class.new(storage) }
describe "success", vcr: "share_point/files_query_userless" do
describe "success", vcr: "sharepoint/files_query_userless" do
it "returns a GroupValidationResult" do
results = validator.call
@@ -25,7 +25,7 @@ module Storages
describe "failure" do
let(:files_double) { class_double(Queries::FilesQuery) }
let(:auth_strategy) { Registry["share_point.authentication.userless"].call }
let(:auth_strategy) { Registry["sharepoint.authentication.userless"].call }
let(:input_data) { Input::Files.build(folder: "/").value! }
let(:result) { Success() }
@@ -34,7 +34,7 @@ module Storages
end
context "when the storage isn't configured" do
let(:storage) { create(:share_point_storage) }
let(:storage) { create(:sharepoint_storage) }
it "the check fails" do
results = validator.call
@@ -44,7 +44,7 @@ module Storages
end
context "when the tenant id is wrong" do
it "but looks like an actual valid value", vcr: "share_point/validation_wrong_tenant_id" do
it "but looks like an actual valid value", vcr: "sharepoint/validation_wrong_tenant_id" do
storage.tenant_id = "itdoesnotexists9000.sharepoint.com"
results = described_class.new(storage).call
@@ -52,7 +52,7 @@ module Storages
expect(results[:tenant_id].code).to eq(:sp_tenant_id_invalid)
end
it "but is blatantly wrong", vcr: "share_point/validation_absurd_tenant_id" do
it "but is blatantly wrong", vcr: "sharepoint/validation_absurd_tenant_id" do
storage.tenant_id = "wrong"
results = described_class.new(storage).call
@@ -62,7 +62,7 @@ module Storages
end
context "when the client secret is wrong" do
it "fails the check", vcr: "share_point/validation_wrong_client_secret" do
it "fails the check", vcr: "sharepoint/validation_wrong_client_secret" do
storage.oauth_client.client_secret = "wrong"
results = described_class.new(storage).call
@@ -72,7 +72,7 @@ module Storages
end
context "when the client id is wrong" do
it "fails the check", vcr: "share_point/validation_wrong_client_id" do
it "fails the check", vcr: "sharepoint/validation_wrong_client_id" do
storage.oauth_client.client_id = "wrong"
results = described_class.new(storage).call
@@ -70,13 +70,13 @@ RSpec.shared_examples_for "file_link contract" do
context "when empty" do
let(:file_link_attributes) { { origin_id: "" } }
include_examples "contract is invalid", origin_id: %i[blank too_short]
include_examples "contract is invalid", origin_id: %i[blank]
end
context "when nil" do
let(:file_link_attributes) { { origin_id: nil } }
include_examples "contract is invalid", origin_id: %i[blank too_short]
include_examples "contract is invalid", origin_id: %i[blank]
end
context "when numeric" do
@@ -96,12 +96,6 @@ RSpec.shared_examples_for "file_link contract" do
include_examples "contract is valid"
end
context "when longer than 100 characters" do
let(:file_link_attributes) { { origin_id: "1" * 201 } }
include_examples "contract is invalid", origin_id: :too_long
end
end
describe "origin_name" do
@@ -232,9 +232,9 @@ FactoryBot.define do
end
end
factory :share_point_storage,
factory :sharepoint_storage,
parent: :storage,
class: "::Storages::SharePointStorage" do
class: "::Storages::SharepointStorage" do
host { "https://openproject.sharepoint.com/sites/ProjectX" }
automatically_managed { false }
@@ -247,8 +247,8 @@ FactoryBot.define do
end
trait :sandbox do
tenant_id { ENV.fetch("SHARE_POINT_TEST_TENANT_ID", "e36f1dbc-fdae-427e-b61b-0d96ddfb81a4") }
host { ENV.fetch("SHARE_POINT_TEST_HOST", "https://ymt6d.sharepoint.com/sites/OPTest") }
tenant_id { ENV.fetch("SHAREPOINT_TEST_TENANT_ID", "e36f1dbc-fdae-427e-b61b-0d96ddfb81a4") }
host { ENV.fetch("SHAREPOINT_TEST_HOST", "https://ymt6d.sharepoint.com/sites/OPTest") }
transient do
oauth_client_token_user { association :user }
@@ -256,18 +256,18 @@ FactoryBot.define do
after(:create) do |storage, evaluator|
create(:oauth_client,
client_id: ENV.fetch("SHARE_POINT_TEST_OAUTH_CLIENT_ID",
client_id: ENV.fetch("SHAREPOINT_TEST_OAUTH_CLIENT_ID",
"MISSING_SHARE_POINT_TEST_OAUTH_CLIENT_ID"),
client_secret: ENV.fetch("SHARE_POINT_TEST_OAUTH_CLIENT_SECRET",
client_secret: ENV.fetch("SHAREPOINT_TEST_OAUTH_CLIENT_SECRET",
"MISSING_SHARE_POINT_TEST_OAUTH_CLIENT_SECRET"),
integration: storage)
create(:oauth_client_token,
oauth_client: storage.oauth_client,
user: evaluator.oauth_client_token_user,
access_token: ENV.fetch("SHARE_POINT_TEST_OAUTH_CLIENT_ACCESS_TOKEN",
access_token: ENV.fetch("SHAREPOINT_TEST_OAUTH_CLIENT_ACCESS_TOKEN",
"MISSING_SHARE_POINT_TEST_OAUTH_CLIENT_ACCESS_TOKEN"),
refresh_token: ENV.fetch("SHARE_POINT_TEST_OAUTH_CLIENT_REFRESH_TOKEN",
refresh_token: ENV.fetch("SHAREPOINT_TEST_OAUTH_CLIENT_REFRESH_TOKEN",
"MISSING_SHARE_POINT_TEST_OAUTH_CLIENT_REFRESH_TOKEN"),
token_type: "bearer")
end