Ejemplo n.º 1
0
        public async Task ChangeMachineTenantAsync_WhenSuccessfulChange_ReturnsUpdatedMachine()
        {
            var machineId = Guid.NewGuid();

            _machineRepositoryMock.SetupCreateItemQuery(o => new List <Machine> {
                new Machine {
                    Id = machineId
                }
            });

            _tenantApiMock.Setup(m => m.GetTenantByIdAsync(It.IsAny <Guid>()))
            .ReturnsAsync(MicroserviceResponse.Create(HttpStatusCode.OK, new Tenant()));

            _cloudShimMock.Setup(m => m.ValidateSettingProfileId(It.IsAny <Guid>(), It.IsAny <Guid>()))
            .ReturnsAsync(MicroserviceResponse.Create(HttpStatusCode.OK, true));

            _machineRepositoryMock.Setup(m => m.UpdateItemAsync(It.IsAny <Guid>(), It.IsAny <Machine>(), It.IsAny <UpdateOptions>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync((Guid i, Machine m, UpdateOptions o, CancellationToken t) => m);

            var request = new ChangeMachineTenantRequest
            {
                Id               = machineId,
                TenantId         = Guid.NewGuid(),
                SettingProfileId = Guid.NewGuid()
            };
            var result = await _controller.ChangeMachineTenantAsync(request, null, CancellationToken.None);

            Assert.NotNull(result);
        }
Ejemplo n.º 2
0
        public async Task ChangeMachineTenantAsync_WhenInvalidSettingProfileId_ThrowsValidationFailedException()
        {
            var machineId = Guid.NewGuid();

            _machineRepositoryMock.SetupCreateItemQuery(o => new List <Machine> {
                new Machine {
                    Id = machineId
                }
            });

            _tenantApiMock.Setup(m => m.GetTenantByIdAsync(It.IsAny <Guid>()))
            .ReturnsAsync((Guid tenantId) => MicroserviceResponse.Create(HttpStatusCode.OK, new Tenant {
                Id = tenantId
            }));

            _cloudShimMock.Setup(m => m.ValidateSettingProfileId(It.IsAny <Guid>(), It.IsAny <Guid>()))
            .ReturnsAsync(MicroserviceResponse.Create(HttpStatusCode.OK, false));

            var request = new ChangeMachineTenantRequest
            {
                Id               = machineId,
                TenantId         = Guid.NewGuid(),
                SettingProfileId = Guid.NewGuid()
            };

            var ex = await Assert.ThrowsAsync <ValidationFailedException>(() => _controller.ChangeMachineTenantAsync(request, null));

            Assert.Collection(ex.Errors, failure => Assert.Equal(nameof(ChangeMachineTenantRequest.SettingProfileId), failure.PropertyName));
        }
Ejemplo n.º 3
0
        public async Task ChangeMachineTenantAsync_WhenMachineNotFound_ThrowsNotFoundException()
        {
            _machineRepositoryMock.SetupCreateItemQuery();

            var request = new ChangeMachineTenantRequest
            {
                Id               = Guid.NewGuid(),
                TenantId         = Guid.NewGuid(),
                SettingProfileId = Guid.NewGuid()
            };

            await Assert.ThrowsAsync <NotFoundException>(() => _controller.ChangeMachineTenantAsync(request, null));
        }
Ejemplo n.º 4
0
        public async Task ChangeMachineTenantAsync_WhenEmptyTenantId_ThrowsValidationFailedException()
        {
            var request = new ChangeMachineTenantRequest
            {
                Id               = Guid.NewGuid(),
                TenantId         = Guid.Empty,
                SettingProfileId = Guid.NewGuid()
            };

            var ex = await Assert.ThrowsAsync <ValidationFailedException>(() => _controller.ChangeMachineTenantAsync(request, null));

            Assert.Collection(ex.Errors, failure => Assert.Equal(nameof(ChangeMachineTenantRequest.TenantId), failure.PropertyName));
        }
        public async Task <Machine> ChangeMachineTenantAsync(ChangeMachineTenantRequest request, Guid?sourceTenantId, CancellationToken cancellationToken)
        {
            // This operation should only be performed by superadmins. While permissions to this
            // operation should be gated by policy documents, we need to make sure that if someone
            // gets this far as a non-superadmin, we have additional checks.

            // We're also assuming here that the existingMachine parameter has been retrieved from
            // this controller using GetMachineByIdAsync.

            // First, let's validate the target tenant ID because this is a free-form text field in
            // the Admin.
            if (request.TenantId == Guid.Empty)
            {
                throw new ValidationFailedException(new[] { new ValidationFailure(nameof(request.TenantId), "The TenantId must not be empty") });
            }

            var machineRepository = await _machineRepositoryAsyncLazy;
            var batchOptions      = sourceTenantId == null
                ? new BatchOptions {
                EnableCrossPartitionQuery = true
            }
                : new BatchOptions {
                PartitionKey = new PartitionKey(sourceTenantId.Value)
            };

            var existingMachine = await machineRepository.CreateItemQuery(batchOptions)
                                  .FirstOrDefaultAsync(m => m.Id == request.Id, cancellationToken);

            if (existingMachine == null)
            {
                throw new NotFoundException($"Unable to find machine with identifier '{request.Id}'");
            }

            if (existingMachine.TenantId == request.TenantId)
            {
                // Nothing to change.
                return(existingMachine);
            }

            // Validate the target tenant by asking the Tenant service.
            var tenantResponse = await _tenantApi.GetTenantByIdAsync(request.TenantId);

            if (!tenantResponse.IsSuccess())
            {
                if (tenantResponse.ResponseCode != HttpStatusCode.NotFound)
                {
                    _logger.Error($"Failed to query the target tenant with identifier '{request.TenantId}' while attempting to move the machine '{request.Id}' to that tenant: ({tenantResponse.ResponseCode}) {tenantResponse.ErrorResponse?.Message ?? tenantResponse.ReasonPhrase}");
                }

                throw new ValidationFailedException(new[] { new ValidationFailure(nameof(request.TenantId), "The TenantId provided in the request is either invalid or cannot be validated") });
            }

            // If the user didn't specify a setting profile identifier, we will pick the first
            // valid setting profile from the target tenant.
            var validateSettingProfileId = true;

            if (request.SettingProfileId == Guid.Empty)
            {
                var settingProfileIdsResponse = await _cloudShim.GetSettingProfileIdsForTenant(request.TenantId);

                if (!settingProfileIdsResponse.IsSuccess() || !settingProfileIdsResponse.Payload.Any())
                {
                    throw new ValidationFailedException(new[] { new ValidationFailure(nameof(request.SettingProfileId), "Unable to find a valid setting profile for the machine in the new tenant") });
                }

                request.SettingProfileId = settingProfileIdsResponse.Payload.First();

                // We don't need to validate a setting profile that we just obtained.
                validateSettingProfileId = false;
            }

            if (validateSettingProfileId && !await IsValidSettingProfileAsync(request.TenantId, request.SettingProfileId))
            {
                throw new ValidationFailedException(new[] { new ValidationFailure(nameof(request.SettingProfileId), "The setting profile identifier is invalid or could not be validated") });
            }

            existingMachine.TenantId         = request.TenantId;
            existingMachine.SettingProfileId = request.SettingProfileId;

            return(await machineRepository.UpdateItemAsync(request.Id, existingMachine, cancellationToken : cancellationToken));
        }