NIFI-10380 Added ListDropbox and DropboxCredentialService

This closes #6331

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
krisztina-zsihovszki 2022-08-03 14:45:02 +02:00 committed by exceptionfactory
parent eb68ffad70
commit b1b3a7057c
No known key found for this signature in database
GPG Key ID: 29B6A52D2AAE8DBA
28 changed files with 2431 additions and 0 deletions

View File

@ -2257,6 +2257,11 @@ The following binary components are provided under the Apache Software License v
atinject
Copyright
(ASLv2) The Dropbox Project
The following NOTICE information applies:
The Dropbox Project
Copyright 2013-2021 Dropbox Inc.
************************
Common Development and Distribution License 1.1
************************

View File

@ -862,6 +862,24 @@ language governing permissions and limitations under the License. -->
<version>1.18.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-processors-nar</artifactId>
<version>1.18.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services-nar</artifactId>
<version>1.18.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services-api-nar</artifactId>
<version>1.18.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<!-- dependencies for jaxb/activation/annotation for running NiFi on Java 11 -->
<!-- TODO: remove these once minimum Java version is 11 -->
<dependency>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-bundle</artifactId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-dropbox-processors-nar</artifactId>
<packaging>nar</packaging>
<properties>
<maven.javadoc.skip>true</maven.javadoc.skip>
<source.skip>true</source.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services-api-nar</artifactId>
<version>1.18.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-processors</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,38 @@
nifi-dropbox-processors-nar
Copyright 2015-2022 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
******************
Apache Software License v2
******************
The following binary components are provided under the Apache Software License v2
(ASLv2) The Dropbox Project
The following NOTICE information applies:
The Dropbox Project
Copyright 2013-2021 Dropbox Inc.
(ASLv2) Jackson JSON processor
The following NOTICE information applies:
# Jackson JSON processor
Jackson is a high-performance, Free/Open Source JSON processing library.
It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
been in development since 2007.
It is currently developed by a community of developers, as well as supported
commercially by FasterXML.com.
## Licensing
Jackson core and extension components may licensed under different licenses.
To find the details that apply to this artifact see the accompanying LICENSE file.
For more information, including possible other licensing options, contact
FasterXML.com (http://fasterxml.com).
## Credits
A list of contributors may be found from CREDITS file, which is included
in some artifacts (usually source distributions); but is always available
from the source code management (SCM) system project uses.

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-bundle</artifactId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-dropbox-processors</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-record</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-listed-entity</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services-api</artifactId>
<version>1.18.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.dropbox.core</groupId>
<artifactId>dropbox-core-sdk</artifactId>
<version>${dropbox.client.version}</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-mock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-record-serialization-services</artifactId>
<version>1.18.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-record-serialization-service-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-distributed-cache-client-service-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-schema-registry-service-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services</artifactId>
<version>1.18.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,186 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.processors.dropbox;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.nifi.processor.util.list.ListableEntity;
import org.apache.nifi.serialization.SimpleRecordSchema;
import org.apache.nifi.serialization.record.MapRecord;
import org.apache.nifi.serialization.record.Record;
import org.apache.nifi.serialization.record.RecordField;
import org.apache.nifi.serialization.record.RecordFieldType;
import org.apache.nifi.serialization.record.RecordSchema;
public class DropboxFileInfo implements ListableEntity {
public static final String ID = "dropbox.id";
public static final String PATH = "path";
public static final String FILENAME = "filename";
public static final String SIZE = "dropbox.size";
public static final String TIMESTAMP = "dropbox.timestamp";
public static final String REVISION = "dropbox.revision";
private static final RecordSchema SCHEMA;
static {
final List<RecordField> recordFields = new ArrayList<>();
recordFields.add(new RecordField(ID, RecordFieldType.STRING.getDataType(), false));
recordFields.add(new RecordField(PATH, RecordFieldType.STRING.getDataType(), false));
recordFields.add(new RecordField(FILENAME, RecordFieldType.STRING.getDataType(), false));
recordFields.add(new RecordField(SIZE, RecordFieldType.LONG.getDataType(), false));
recordFields.add(new RecordField(TIMESTAMP, RecordFieldType.LONG.getDataType(), false));
recordFields.add(new RecordField(REVISION, RecordFieldType.STRING.getDataType(), false));
SCHEMA = new SimpleRecordSchema(recordFields);
}
public static RecordSchema getRecordSchema() {
return SCHEMA;
}
private final String id;
private final String path;
private final String filename;
private final long size;
private final long timestamp;
private final String revision;
private DropboxFileInfo(final Builder builder) {
this.id = builder.id;
this.path = builder.path;
this.filename = builder.filename;
this.size = builder.size;
this.timestamp = builder.timestamp;
this.revision = builder.revision;
}
public String getId() {
return id;
}
public String getPath() {
return path;
}
public String getRevision() {
return revision;
}
public String getFileName() {
return filename;
}
@Override
public Record toRecord() {
final Map<String, Object> values = new HashMap<>();
values.put(ID, getId());
values.put(PATH, getPath());
values.put(FILENAME, getFileName());
values.put(SIZE, getSize());
values.put(TIMESTAMP, getTimestamp());
values.put(REVISION, getRevision());
return new MapRecord(SCHEMA, values);
}
@Override
public String getName() {
return getFileName();
}
@Override
public String getIdentifier() {
return getId();
}
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public long getSize() {
return size;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DropboxFileInfo that = (DropboxFileInfo) o;
return id.equals(that.id) && size == that.size && timestamp == that.timestamp && path.equals(that.path) && filename.equals(that.filename)
&& revision.equals(that.revision);
}
@Override
public int hashCode() {
return Objects.hash(id, path, filename, size, timestamp, revision);
}
public static final class Builder {
private String id;
private String path;
private String filename;
private long size;
private long timestamp;
private String revision;
public Builder id(String id) {
this.id = id;
return this;
}
public Builder path(String path) {
this.path = path;
return this;
}
public Builder name(String filename) {
this.filename = filename;
return this;
}
public Builder size(long size) {
this.size = size;
return this;
}
public Builder timestamp(long createdTime) {
this.timestamp = createdTime;
return this;
}
public Builder revision(String revision) {
this.revision = revision;
return this;
}
public DropboxFileInfo build() {
return new DropboxFileInfo(this);
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.processors.dropbox;
import java.util.function.Function;
public enum DropboxFlowFileAttribute {
ID(DropboxFileInfo.ID, DropboxFileInfo::getId),
PATH(DropboxFileInfo.PATH, DropboxFileInfo::getPath),
FILENAME(DropboxFileInfo.FILENAME, DropboxFileInfo::getName),
SIZE(DropboxFileInfo.SIZE, fileInfo -> String.valueOf(fileInfo.getSize())),
TIMESTAMP(DropboxFileInfo.TIMESTAMP, fileInfo -> String.valueOf(fileInfo.getTimestamp())),
REVISION(DropboxFileInfo.REVISION, DropboxFileInfo::getRevision);
private final String name;
private final Function<DropboxFileInfo, String> fromFileInfo;
DropboxFlowFileAttribute(String attributeName, Function<DropboxFileInfo, String> fromFileInfo) {
this.name = attributeName;
this.fromFileInfo = fromFileInfo;
}
public String getName() {
return name;
}
public String getValue(DropboxFileInfo fileInfo) {
return fromFileInfo.apply(fileInfo);
}
}

View File

@ -0,0 +1,293 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.processors.dropbox;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import com.dropbox.core.DbxException;
import com.dropbox.core.DbxRequestConfig;
import com.dropbox.core.oauth.DbxCredential;
import com.dropbox.core.v2.DbxClientV2;
import com.dropbox.core.v2.files.FileMetadata;
import com.dropbox.core.v2.files.ListFolderBuilder;
import com.dropbox.core.v2.files.ListFolderResult;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.apache.nifi.annotation.behavior.InputRequirement;
import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
import org.apache.nifi.annotation.behavior.PrimaryNodeOnly;
import org.apache.nifi.annotation.behavior.Stateful;
import org.apache.nifi.annotation.behavior.TriggerSerially;
import org.apache.nifi.annotation.behavior.WritesAttribute;
import org.apache.nifi.annotation.behavior.WritesAttributes;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.annotation.lifecycle.OnScheduled;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.state.Scope;
import org.apache.nifi.context.PropertyContext;
import org.apache.nifi.dropbox.credentials.service.DropboxCredentialDetails;
import org.apache.nifi.dropbox.credentials.service.DropboxCredentialService;
import org.apache.nifi.expression.ExpressionLanguageScope;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processor.util.list.AbstractListProcessor;
import org.apache.nifi.processor.util.list.ListedEntityTracker;
import org.apache.nifi.serialization.record.RecordSchema;
@PrimaryNodeOnly
@TriggerSerially
@Tags({"dropbox", "storage"})
@CapabilityDescription("Retrieves a listing of files from Dropbox (shortcuts are ignored)." +
" Each listed file may result in one FlowFile, the metadata being written as Flowfile attributes." +
" When the 'Record Writer' property is set, the entire result is written as records to a single FlowFile." +
" This Processor is designed to run on Primary Node only in a cluster. If the primary node changes, the new Primary Node will pick up where the" +
" previous node left off without duplicating all of the data.")
@InputRequirement(Requirement.INPUT_FORBIDDEN)
@WritesAttributes({@WritesAttribute(attribute = DropboxFileInfo.ID, description = "The Dropbox identifier of the file"),
@WritesAttribute(attribute = DropboxFileInfo.PATH, description = "The folder path where the file is located"),
@WritesAttribute(attribute = DropboxFileInfo.FILENAME, description = "The name of the file"),
@WritesAttribute(attribute = DropboxFileInfo.SIZE, description = "The size of the file"),
@WritesAttribute(attribute = DropboxFileInfo.TIMESTAMP, description = "The server modified time, when the file was uploaded to Dropbox"),
@WritesAttribute(attribute = DropboxFileInfo.REVISION, description = "Revision of the file")})
@Stateful(scopes = {Scope.CLUSTER}, description = "The processor stores necessary data to be able to keep track what files have been listed already. " +
"What exactly needs to be stored depends on the 'Listing Strategy'.")
public class ListDropbox extends AbstractListProcessor<DropboxFileInfo> {
public static final PropertyDescriptor FOLDER = new PropertyDescriptor.Builder()
.name("folder")
.displayName("Folder")
.description("The Dropbox identifier or path of the folder from which to pull list of files." +
" 'Folder' should match the following regular expression pattern: /.*|id:.* ." +
" Example for folder identifier: id:odTlUvbpIEAAAAAAAAAGGQ." +
" Example for folder path: /Team1/Task1.")
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.required(true)
.addValidator(StandardValidators.createRegexMatchingValidator(Pattern.compile("/.*|id:.*")))
.defaultValue("/")
.build();
public static final PropertyDescriptor RECURSIVE_SEARCH = new PropertyDescriptor.Builder()
.name("recursive-search")
.displayName("Search Recursively")
.description("Indicates whether to list files from subfolders of the Dropbox folder.")
.required(true)
.defaultValue("true")
.allowableValues("true", "false")
.build();
public static final PropertyDescriptor MIN_AGE = new PropertyDescriptor.Builder()
.name("min-age")
.displayName("Minimum File Age")
.description("The minimum age a file must be in order to be considered; any files newer than this will be ignored.")
.required(true)
.addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
.defaultValue("0 sec")
.build();
public static final PropertyDescriptor CREDENTIAL_SERVICE = new PropertyDescriptor.Builder()
.name("dropbox-credential-service")
.displayName("Dropbox Credential Service")
.description("Controller Service used to obtain Dropbox credentials (App Key, App Secret, Access Token, Refresh Token)." +
" See controller service's Additional Details for more information.")
.identifiesControllerService(DropboxCredentialService.class)
.required(true)
.build();
public static final PropertyDescriptor LISTING_STRATEGY = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(AbstractListProcessor.LISTING_STRATEGY)
.allowableValues(BY_TIMESTAMPS, BY_ENTITIES, BY_TIME_WINDOW, NO_TRACKING)
.build();
public static final PropertyDescriptor TRACKING_STATE_CACHE = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(ListedEntityTracker.TRACKING_STATE_CACHE)
.dependsOn(LISTING_STRATEGY, BY_ENTITIES)
.build();
public static final PropertyDescriptor TRACKING_TIME_WINDOW = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(ListedEntityTracker.TRACKING_TIME_WINDOW)
.dependsOn(LISTING_STRATEGY, BY_ENTITIES)
.build();
public static final PropertyDescriptor INITIAL_LISTING_TARGET = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(ListedEntityTracker.INITIAL_LISTING_TARGET)
.dependsOn(LISTING_STRATEGY, BY_ENTITIES)
.build();
private static final List<PropertyDescriptor> PROPERTIES = Collections.unmodifiableList(Arrays.asList(
CREDENTIAL_SERVICE,
FOLDER,
RECURSIVE_SEARCH,
MIN_AGE,
LISTING_STRATEGY,
TRACKING_STATE_CACHE,
TRACKING_TIME_WINDOW,
INITIAL_LISTING_TARGET,
RECORD_WRITER
));
private DbxClientV2 dropboxApiClient;
@OnScheduled
public void onScheduled(final ProcessContext context) {
dropboxApiClient = getDropboxApiClient(context);
}
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return PROPERTIES;
}
@Override
protected Map<String, String> createAttributes(
final DropboxFileInfo entity,
final ProcessContext context) {
final Map<String, String> attributes = new HashMap<>();
for (DropboxFlowFileAttribute attribute : DropboxFlowFileAttribute.values()) {
Optional.ofNullable(attribute.getValue(entity))
.ifPresent(value -> attributes.put(attribute.getName(), value));
}
return attributes;
}
@Override
protected String getPath(final ProcessContext context) {
return context.getProperty(FOLDER).evaluateAttributeExpressions().getValue();
}
protected DbxClientV2 getDropboxApiClient(ProcessContext context) {
final DropboxCredentialService credentialService = context.getProperty(CREDENTIAL_SERVICE)
.asControllerService(DropboxCredentialService.class);
DbxRequestConfig config = new DbxRequestConfig(format("%s-%s", getClass().getSimpleName(), getIdentifier()));
DropboxCredentialDetails credential = credentialService.getDropboxCredential();
return new DbxClientV2(config, new DbxCredential(credential.getAccessToken(), -1L,
credential.getRefreshToken(), credential.getAppKey(), credential.getAppSecret()));
}
@Override
protected List<DropboxFileInfo> performListing(ProcessContext context, Long minTimestamp,
ListingMode listingMode) throws IOException {
final List<DropboxFileInfo> listing = new ArrayList<>();
final String folderName = getPath(context);
final Boolean recursive = context.getProperty(RECURSIVE_SEARCH).asBoolean();
final Long minAge = context.getProperty(MIN_AGE).asTimePeriod(TimeUnit.MILLISECONDS);
try {
Predicate<FileMetadata> metadataFilter = createMetadataFilter(minTimestamp, minAge);
ListFolderBuilder listFolderBuilder = dropboxApiClient.files().listFolderBuilder(convertFolderName(folderName));
ListFolderResult result = listFolderBuilder
.withRecursive(recursive)
.start();
List<FileMetadata> metadataList = new ArrayList<>(filterMetadata(result, metadataFilter));
while (result.getHasMore()) {
result = dropboxApiClient.files().listFolderContinue(result.getCursor());
metadataList.addAll(filterMetadata(result, metadataFilter));
}
for (FileMetadata metadata : metadataList) {
DropboxFileInfo.Builder builder = new DropboxFileInfo.Builder()
.id(metadata.getId())
.path(getParentPath(metadata.getPathDisplay()))
.name(metadata.getName())
.size(metadata.getSize())
.timestamp(metadata.getServerModified().getTime())
.revision(metadata.getRev());
listing.add(builder.build());
}
} catch (DbxException e) {
throw new IOException("Failed to list Dropbox folder [" + folderName + "]", e);
}
return listing;
}
@Override
protected boolean isListingResetNecessary(final PropertyDescriptor property) {
return LISTING_STRATEGY.equals(property)
|| FOLDER.equals(property)
|| RECURSIVE_SEARCH.equals(property);
}
@Override
protected Scope getStateScope(final PropertyContext context) {
return Scope.CLUSTER;
}
@Override
protected RecordSchema getRecordSchema() {
return DropboxFileInfo.getRecordSchema();
}
@Override
protected Integer countUnfilteredListing(final ProcessContext context) throws IOException {
return performListing(context, null, ListingMode.CONFIGURATION_VERIFICATION).size();
}
@Override
protected String getListingContainerName(final ProcessContext context) {
return format("Dropbox Folder [%s]", getPath(context));
}
private Predicate<FileMetadata> createMetadataFilter(Long minTimestamp, Long minAge) {
Predicate<FileMetadata> metadataFilter = FileMetadata::getIsDownloadable;
if (minTimestamp != null && minTimestamp > 0) {
metadataFilter = metadataFilter.and(metadata -> metadata.getServerModified().getTime() >= minTimestamp);
}
if (minAge != null && minAge > 0) {
long maxTimestamp = System.currentTimeMillis() - minAge;
metadataFilter = metadataFilter.and(metadata -> metadata.getServerModified().getTime() < maxTimestamp);
}
return metadataFilter;
}
private List<FileMetadata> filterMetadata(ListFolderResult result, Predicate<FileMetadata> metadataFilter) {
return result.getEntries().stream()
.filter(metadata -> metadata instanceof FileMetadata)
.map(FileMetadata.class::cast)
.filter(metadataFilter)
.collect(toList());
}
private String getParentPath(String fullPath) {
int idx = fullPath.lastIndexOf("/");
String parentPath = fullPath.substring(0, idx);
return "".equals(parentPath) ? "/" : parentPath;
}
private String convertFolderName(String folderName) {
return "/".equals(folderName) ? "" : folderName;
}
}

View File

@ -0,0 +1,15 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
org.apache.nifi.processors.dropbox.ListDropbox

View File

@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.processors.dropbox;
import com.dropbox.core.DbxRequestConfig;
import com.dropbox.core.oauth.DbxCredential;
import com.dropbox.core.v2.DbxClientV2;
import com.dropbox.core.v2.files.FileMetadata;
import com.dropbox.core.v2.files.GetMetadataErrorException;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import org.apache.nifi.processor.Processor;
import org.apache.nifi.services.dropbox.StandardDropboxCredentialService;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
/**
* Set the following constants before running: <br/>
* <br/> APPLICATION_KEY - App Key of your app
* <br/> APPLICATION_SECRET - Your App Secret of your app
* <br/> ACCESS_TOKEN - Access Token generated for your app
* <br/> REFRESH_TOKEN - Refresh Token generated for your app
* <br/><br/>
* For App Key, App Secret, Access Token and Refresh Token generation please check controller service's Additional Details page.<br/><br/>
* NOTE: Since the integration test creates the test files you need "files.content.write" permission besides the "files.content.read"
* permission mentioned in controller service's Additional Details page.<br/>
*
* <br/> Created files and folders are cleaned up, but it's advisable to dedicate a folder for this test so that it can
* be cleaned up easily should the test fail to do so. <br/>
* <br/> WARNING: The creation of a file is not a synchronized operation, may need to adjust tests accordingly!
*/
public abstract class AbstractDropboxIT<T extends Processor> {
public static final String APP_KEY = "";
public static final String APP_SECRET = "";
public static final String ACCESS_TOKEN = "";
public static final String REFRESH_TOKEN = "";
public static final String MAIN_FOLDER = "/testFolder";
protected T testSubject;
protected TestRunner testRunner;
protected String mainFolderId;
private DbxClientV2 client;
@AfterEach
public void teardown() throws Exception {
deleteFolderIfExists(MAIN_FOLDER);
}
protected abstract T createTestSubject();
@BeforeEach
protected void init() throws Exception {
testSubject = createTestSubject();
testRunner = createTestRunner();
DbxCredential credential =
new DbxCredential(ACCESS_TOKEN, -1L, REFRESH_TOKEN, APP_KEY, APP_SECRET);
DbxRequestConfig config = new DbxRequestConfig("nifi");
client = new DbxClientV2(config, credential);
mainFolderId = createFolder(MAIN_FOLDER);
}
protected TestRunner createTestRunner() throws Exception {
TestRunner testRunner = TestRunners.newTestRunner(testSubject);
StandardDropboxCredentialService controllerService = new StandardDropboxCredentialService();
testRunner.addControllerService("dropbox_credential_provider_service", controllerService);
testRunner.setProperty(controllerService, StandardDropboxCredentialService.APP_KEY, APP_KEY);
testRunner.setProperty(controllerService, StandardDropboxCredentialService.APP_SECRET, APP_SECRET);
testRunner.setProperty(controllerService, StandardDropboxCredentialService.ACCESS_TOKEN, ACCESS_TOKEN);
testRunner.setProperty(controllerService, StandardDropboxCredentialService.REFRESH_TOKEN, REFRESH_TOKEN);
testRunner.enableControllerService(controllerService);
testRunner.setProperty(ListDropbox.CREDENTIAL_SERVICE, "dropbox_credential_provider_service");
return testRunner;
}
protected void deleteFolderIfExists(String path) throws Exception {
if (folderExists(path)) {
client.files().deleteV2(path);
}
}
protected FileMetadata createFile(String name, String fileContent, String folder) throws Exception {
ByteArrayInputStream content = new ByteArrayInputStream(fileContent.getBytes(StandardCharsets.UTF_8));
return client.files().upload(folder + "/" + name).uploadAndFinish(content);
}
private String createFolder(String path) throws Exception {
if (folderExists(path)) {
deleteFolder(path);
}
return client.files().createFolderV2(path).getMetadata().getId();
}
private void deleteFolder(String path) throws Exception {
client.files().deleteV2(path);
}
private boolean folderExists(String path) throws Exception {
try {
return client.files().getMetadata(path) != null;
} catch (GetMetadataErrorException e) {
if (e.errorValue.isPath() && e.errorValue.getPathValue().isNotFound()) {
return false;
}
throw e;
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.processors.dropbox;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.apache.nifi.util.MockFlowFile;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class ListDropboxIT extends AbstractDropboxIT<ListDropbox> {
private static final String NOT_MAIN_FOLDER = "/notMainFolder";
private static final String YOUNG_FILE_NAME = "just_created";
@BeforeEach
public void init() throws Exception {
super.init();
testRunner.setProperty(ListDropbox.FOLDER, MAIN_FOLDER);
}
@AfterEach
public void teardown() throws Exception {
super.teardown();
deleteFolderIfExists(NOT_MAIN_FOLDER);
}
@Override
protected ListDropbox createTestSubject() {
return new ListDropbox();
}
@Test
void testEmbeddedDirectoriesAreListed() throws Exception {
createFile("test_file1", "test_file_content1", MAIN_FOLDER);
createFile("test_file2", "test_file_content2", MAIN_FOLDER);
createFile("test_file11", "test_file_content11", MAIN_FOLDER + "/testFolder1");
createFile("test_file112", "test_file_content112", MAIN_FOLDER + "/testFolder2");
createFile("test_file_not_in_main_folder", "test_file_content31", NOT_MAIN_FOLDER);
List<String> expectedFileNames = Arrays.asList("test_file1", "test_file2", "test_file11", "test_file112");
waitForFileCreation();
testRunner.run();
List<MockFlowFile> successFlowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
List<String> actualFileNames = getFilenames(successFlowFiles);
assertEquals(expectedFileNames, actualFileNames);
}
@Test
void testFolderIsListedById() throws Exception {
testRunner.setProperty(ListDropbox.FOLDER, mainFolderId);
createFile("test_file1", "test_file_content1", MAIN_FOLDER);
createFile("test_file11", "test_file_content11", MAIN_FOLDER + "/testFolder1");
List<String> expectedFileNames = Arrays.asList("test_file1", "test_file11");
waitForFileCreation();
testRunner.run();
List<MockFlowFile> successFlowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
List<String> actualFileNames = getFilenames(successFlowFiles);
assertEquals(expectedFileNames, actualFileNames);
}
@Test
void testTooYoungFilesNotListedWhenMinAgeIsSet() throws Exception {
testRunner.setProperty(ListDropbox.MIN_AGE, "15 s");
createFile(YOUNG_FILE_NAME, "test_file_content1", MAIN_FOLDER);
waitForFileCreation();
testRunner.run();
List<MockFlowFile> successFlowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
List<String> actualFileNames = getFilenames(successFlowFiles);
assertEquals(emptyList(), actualFileNames);
// Next, wait for another 10+ seconds for MIN_AGE to expire then list again.
Thread.sleep(10000);
List<String> expectedFileNames = singletonList(YOUNG_FILE_NAME);
testRunner.run();
successFlowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
actualFileNames = getFilenames(successFlowFiles);
assertEquals(expectedFileNames, actualFileNames);
}
private void waitForFileCreation() throws InterruptedException {
// We need to wait since the creation of the files are not (completely) synchronized.
Thread.sleep(5000);
}
private List<String> getFilenames(List<MockFlowFile> flowFiles) {
return flowFiles.stream()
.map(flowFile -> flowFile.getAttribute("filename"))
.collect(toList());
}
}

View File

@ -0,0 +1,285 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.processors.dropbox;
import static java.util.Collections.singletonList;
import static java.util.Spliterators.spliteratorUnknownSize;
import static java.util.stream.Collectors.toList;
import static org.apache.nifi.services.dropbox.StandardDropboxCredentialService.ACCESS_TOKEN;
import static org.apache.nifi.services.dropbox.StandardDropboxCredentialService.APP_KEY;
import static org.apache.nifi.services.dropbox.StandardDropboxCredentialService.APP_SECRET;
import static org.apache.nifi.services.dropbox.StandardDropboxCredentialService.REFRESH_TOKEN;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import com.dropbox.core.DbxException;
import com.dropbox.core.v2.DbxClientV2;
import com.dropbox.core.v2.files.DbxUserFilesRequests;
import com.dropbox.core.v2.files.FileMetadata;
import com.dropbox.core.v2.files.FolderMetadata;
import com.dropbox.core.v2.files.ListFolderBuilder;
import com.dropbox.core.v2.files.ListFolderResult;
import com.dropbox.core.v2.files.Metadata;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Spliterator;
import java.util.stream.StreamSupport;
import org.apache.nifi.json.JsonRecordSetWriter;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.serialization.RecordSetWriterFactory;
import org.apache.nifi.services.dropbox.StandardDropboxCredentialService;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class ListDropboxTest {
public static final String ID_1 = "id:11111";
public static final String ID_2 = "id:22222";
public static final String TEST_FOLDER = "/testFolder";
public static final String FILENAME_1 = "file_name_1";
public static final String FILENAME_2 = "file_name_2";
public static final long SIZE = 125;
public static final long CREATED_TIME = 1659707000;
public static final String REVISION = "5e4ddb1320676a5c29261";
public static final boolean IS_RECURSIVE = true;
public static final long MIN_TIMESTAMP = 1659707000;
public static final long OLD_CREATED_TIME = 1657375066;
private TestRunner testRunner;
@Mock
private DbxClientV2 mockDropboxClient;
@Mock
private StandardDropboxCredentialService credentialService;
@Mock
private DbxUserFilesRequests mockDbxUserFilesRequest;
@Mock
private ListFolderResult mockListFolderResult;
@Mock
private ListFolderBuilder mockListFolderBuilder;
@BeforeEach
void setUp() throws Exception {
ListDropbox testSubject = new ListDropbox() {
@Override
public DbxClientV2 getDropboxApiClient(ProcessContext context) {
return mockDropboxClient;
}
@Override
protected List<DropboxFileInfo> performListing(
ProcessContext context, Long minTimestamp, ListingMode ignoredListingMode) throws IOException {
return super.performListing(context, MIN_TIMESTAMP, ListingMode.EXECUTION);
}
};
testRunner = TestRunners.newTestRunner(testSubject);
mockStandardDropboxCredentialService();
testRunner.setProperty(ListDropbox.RECURSIVE_SEARCH, Boolean.toString(IS_RECURSIVE));
testRunner.setProperty(ListDropbox.MIN_AGE, "0 sec");
}
@Test
void testFolderValidity() {
testRunner.setProperty(ListDropbox.FOLDER, "id:odTlUvbpIEAAAAAAAAABmw");
testRunner.assertValid();
testRunner.setProperty(ListDropbox.FOLDER, "/");
testRunner.assertValid();
testRunner.setProperty(ListDropbox.FOLDER, "/tempFolder");
testRunner.assertValid();
testRunner.setProperty(ListDropbox.FOLDER, "/tempFolder/tempSubFolder");
testRunner.assertValid();
testRunner.setProperty(ListDropbox.FOLDER, "/tempFolder/tempSubFolder/");
testRunner.assertValid();
testRunner.setProperty(ListDropbox.FOLDER, "tempFolder");
testRunner.assertNotValid();
testRunner.setProperty(ListDropbox.FOLDER, "odTlUvbpIEAAAAAAAAABmw");
testRunner.assertNotValid();
testRunner.setProperty(ListDropbox.FOLDER, "");
testRunner.assertNotValid();
}
@Test
void testRootIsListed() throws Exception {
mockFileListing();
String folderName = "/";
testRunner.setProperty(ListDropbox.FOLDER, folderName);
//root is listed when "" is used in Dropbox API
when(mockDbxUserFilesRequest.listFolderBuilder("")).thenReturn(mockListFolderBuilder);
when(mockListFolderResult.getEntries()).thenReturn(singletonList(
createFileMetaData(FILENAME_1, folderName, ID_1, CREATED_TIME)
));
testRunner.run();
testRunner.assertAllFlowFilesTransferred(ListDropbox.REL_SUCCESS, 1);
List<MockFlowFile> flowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
MockFlowFile ff0 = flowFiles.get(0);
assertFlowFileAttributes(ff0, folderName);
}
@Test
void testOnlyFilesAreListedFolderIsFiltered() throws Exception {
mockFileListing();
testRunner.setProperty(ListDropbox.FOLDER, TEST_FOLDER);
when(mockDbxUserFilesRequest.listFolderBuilder(TEST_FOLDER)).thenReturn(mockListFolderBuilder);
when(mockListFolderResult.getEntries()).thenReturn(Arrays.asList(
createFileMetaData(FILENAME_1, TEST_FOLDER, ID_1, CREATED_TIME),
createFolderMetaData("testFolder1", TEST_FOLDER)
));
testRunner.run();
testRunner.assertAllFlowFilesTransferred(ListDropbox.REL_SUCCESS, 1);
List<MockFlowFile> flowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
MockFlowFile ff0 = flowFiles.get(0);
assertFlowFileAttributes(ff0, TEST_FOLDER);
}
@Test
void testOldItemIsFiltered() throws Exception {
mockFileListing();
testRunner.setProperty(ListDropbox.FOLDER, TEST_FOLDER);
when(mockDbxUserFilesRequest.listFolderBuilder(TEST_FOLDER)).thenReturn(mockListFolderBuilder);
when(mockListFolderResult.getEntries()).thenReturn(Arrays.asList(
createFileMetaData(FILENAME_1, TEST_FOLDER, ID_1, CREATED_TIME),
createFileMetaData(FILENAME_2, TEST_FOLDER, ID_2, OLD_CREATED_TIME)
));
testRunner.run();
testRunner.assertAllFlowFilesTransferred(ListDropbox.REL_SUCCESS, 1);
List<MockFlowFile> flowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
MockFlowFile ff0 = flowFiles.get(0);
assertFlowFileAttributes(ff0, TEST_FOLDER);
}
@Test
void testRecordWriter() throws Exception {
mockFileListing();
mockRecordWriter();
testRunner.setProperty(ListDropbox.FOLDER, TEST_FOLDER);
when(mockDbxUserFilesRequest.listFolderBuilder(TEST_FOLDER)).thenReturn(mockListFolderBuilder);
when(mockListFolderResult.getEntries()).thenReturn(Arrays.asList(
createFileMetaData(FILENAME_1, TEST_FOLDER, ID_1, CREATED_TIME),
createFileMetaData(FILENAME_2, TEST_FOLDER, ID_2, CREATED_TIME)
));
testRunner.run();
testRunner.assertAllFlowFilesTransferred(ListDropbox.REL_SUCCESS, 1);
List<MockFlowFile> flowFiles = testRunner.getFlowFilesForRelationship(ListDropbox.REL_SUCCESS);
MockFlowFile ff0 = flowFiles.get(0);
List<String> expectedFileNames = Arrays.asList(FILENAME_1, FILENAME_2);
List<String> actualFileNames = getFilenames(ff0.getContent());
assertEquals(expectedFileNames, actualFileNames);
}
private void assertFlowFileAttributes(MockFlowFile flowFile, String folderName) {
flowFile.assertAttributeEquals(DropboxFileInfo.ID, ID_1);
flowFile.assertAttributeEquals(DropboxFileInfo.FILENAME, FILENAME_1);
flowFile.assertAttributeEquals(DropboxFileInfo.PATH, folderName);
flowFile.assertAttributeEquals(DropboxFileInfo.TIMESTAMP, Long.toString(CREATED_TIME));
flowFile.assertAttributeEquals(DropboxFileInfo.SIZE, Long.toString(SIZE));
flowFile.assertAttributeEquals(DropboxFileInfo.REVISION, REVISION);
}
private FileMetadata createFileMetaData(
String filename,
String parent,
String id,
long createdTime) {
return FileMetadata.newBuilder(filename, id,
new Date(createdTime),
new Date(createdTime),
REVISION, SIZE)
.withPathDisplay(parent + "/" + filename)
.build();
}
private Metadata createFolderMetaData(String folderName, String parent) {
return FolderMetadata.newBuilder(folderName)
.withPathDisplay(parent + "/" + folderName)
.build();
}
private void mockStandardDropboxCredentialService() throws Exception {
String credentialServiceId = "dropbox_credentials";
when(credentialService.getIdentifier()).thenReturn(credentialServiceId);
testRunner.addControllerService(credentialServiceId, credentialService);
testRunner.setProperty(credentialService, APP_KEY, "appKey");
testRunner.setProperty(credentialService, APP_SECRET, "appSecret");
testRunner.setProperty(credentialService, ACCESS_TOKEN, "accessToken");
testRunner.setProperty(credentialService, REFRESH_TOKEN, "refreshToken");
testRunner.enableControllerService(credentialService);
testRunner.setProperty(ListDropbox.CREDENTIAL_SERVICE, credentialServiceId);
}
private void mockRecordWriter() throws InitializationException {
RecordSetWriterFactory recordWriter = new JsonRecordSetWriter();
testRunner.addControllerService("record_writer", recordWriter);
testRunner.enableControllerService(recordWriter);
testRunner.setProperty(ListDropbox.RECORD_WRITER, "record_writer");
}
private void mockFileListing() throws DbxException {
when(mockListFolderBuilder.withRecursive(IS_RECURSIVE)).thenReturn(mockListFolderBuilder);
when(mockListFolderBuilder.start()).thenReturn(mockListFolderResult);
when(mockDropboxClient.files()).thenReturn(mockDbxUserFilesRequest);
when(mockListFolderResult.getHasMore()).thenReturn(false);
}
private List<String> getFilenames(String flowFileContent) {
try {
JsonNode jsonNode = new ObjectMapper().readTree(flowFileContent);
return StreamSupport.stream(spliteratorUnknownSize(jsonNode.iterator(), Spliterator.ORDERED), false)
.map(node -> node.get("filename").asText())
.collect(toList());
} catch (JsonProcessingException e) {
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>nifi-dropbox-bundle</artifactId>
<groupId>org.apache.nifi</groupId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-dropbox-services-api-nar</artifactId>
<version>1.18.0-SNAPSHOT</version>
<packaging>nar</packaging>
<properties>
<maven.javadoc.skip>true</maven.javadoc.skip>
<source.skip>true</source.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services-api</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-services-api-nar</artifactId>
<version>1.18.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,5 @@
nifi-dropbox-services-api-nar
Copyright 2015-2022 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>nifi-dropbox-bundle</artifactId>
<groupId>org.apache.nifi</groupId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-dropbox-services-api</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.dropbox.credentials.service;
public class DropboxCredentialDetails {
private final String appKey;
private final String appSecret;
private final String accessToken;
private final String refreshToken;
public DropboxCredentialDetails(String appKey, String appSecret, String accessToken, String refreshToken) {
this.appKey = appKey;
this.appSecret = appSecret;
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
public String getAppKey() {
return appKey;
}
public String getAppSecret() {
return appSecret;
}
public String getAccessToken() {
return accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
}

View File

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.dropbox.credentials.service;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.controller.ControllerService;
/**
* DropboxCredentialService interface to support getting Dropbox credentials (app key, app secret, access token, refresh token)
*
*
* @see <a href="https://www.dropbox.com/developers/reference/getting-started">Dropbox Developers Getting Started</a>
* @see <a href="https://dropbox.github.io/dropbox-sdk-java/api-docs/v4.0.0/com/dropbox/core/oauth/DbxCredential.html">DbxCredential</a>
*/
@Tags({"dropbox", "credentials", "auth", "session"})
@CapabilityDescription("Provides DropboxCredentialDetails.")
public interface DropboxCredentialService extends ControllerService {
/**
* Get Dropbox credentials
* @return DropboxCredentialDetails object which contains app key, app secret, access and refresh tokens for authorizing
* requests on the Dropbox platform.
*/
DropboxCredentialDetails getDropboxCredential();
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-bundle</artifactId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-dropbox-services-nar</artifactId>
<packaging>nar</packaging>
<properties>
<maven.javadoc.skip>true</maven.javadoc.skip>
<source.skip>true</source.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services-api-nar</artifactId>
<version>1.18.0-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,5 @@
nifi-dropbox-services-nar
Copyright 2015-2022 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-bundle</artifactId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-dropbox-services</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-dropbox-services-api</artifactId>
<version>1.18.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,112 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.services.dropbox;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.annotation.lifecycle.OnEnabled;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.controller.AbstractControllerService;
import org.apache.nifi.controller.ConfigurationContext;
import org.apache.nifi.dropbox.credentials.service.DropboxCredentialDetails;
import org.apache.nifi.dropbox.credentials.service.DropboxCredentialService;
import org.apache.nifi.expression.ExpressionLanguageScope;
import org.apache.nifi.processor.util.StandardValidators;
@CapabilityDescription("Defines credentials for Dropbox processors.")
@Tags({"dropbox", "credentials", "provider"})
public class StandardDropboxCredentialService extends AbstractControllerService implements DropboxCredentialService {
public static final PropertyDescriptor APP_KEY = new PropertyDescriptor.Builder()
.name("app-key")
.displayName("App Key")
.description("App Key of the user's Dropbox app." +
" See Additional Details for more information.")
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.required(true)
.build();
public static final PropertyDescriptor APP_SECRET = new PropertyDescriptor.Builder()
.name("app-secret")
.displayName("App Secret")
.description("App Secret of the user's Dropbox app." +
" See Additional Details for more information.")
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.sensitive(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.required(true)
.build();
public static final PropertyDescriptor ACCESS_TOKEN = new PropertyDescriptor.Builder()
.name("access-token")
.displayName("Access Token")
.description("Access Token of the user's Dropbox app." +
" See Additional Details for more information about Access Token generation.")
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.sensitive(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.required(true)
.build();
public static final PropertyDescriptor REFRESH_TOKEN = new PropertyDescriptor.Builder()
.name("refresh-token")
.displayName("Refresh Token")
.description("Refresh Token of the user's Dropbox app." +
" See Additional Details for more information about Refresh Token generation.")
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
.sensitive(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.required(true)
.build();
private static final List<PropertyDescriptor> PROPERTIES;
static {
final List<PropertyDescriptor> props = new ArrayList<>();
props.add(APP_KEY);
props.add(APP_SECRET);
props.add(ACCESS_TOKEN);
props.add(REFRESH_TOKEN);
PROPERTIES = Collections.unmodifiableList(props);
}
private DropboxCredentialDetails credential;
@Override
public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return PROPERTIES;
}
@OnEnabled
public void onEnabled(final ConfigurationContext context) {
final String appKey = context.getProperty(APP_KEY).evaluateAttributeExpressions().getValue();
final String appSecret = context.getProperty(APP_SECRET).evaluateAttributeExpressions().getValue();
final String accessToken = context.getProperty(ACCESS_TOKEN).evaluateAttributeExpressions().getValue();
final String refreshToken = context.getProperty(REFRESH_TOKEN).evaluateAttributeExpressions().getValue();
this.credential = new DropboxCredentialDetails(appKey, appSecret, accessToken, refreshToken);
}
@Override
public DropboxCredentialDetails getDropboxCredential() {
return credential;
}
}

View File

@ -0,0 +1,15 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
org.apache.nifi.services.dropbox.StandardDropboxCredentialService

View File

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<head>
<meta charset="utf-8"/>
<title>StandardDropboxCredentialService</title>
<link rel="stylesheet" href="/nifi-docs/css/component-usage.css" type="text/css"/>
</head>
<body>
<h1>Generating credentials for Dropbox authentication</h1>
<p>
StandardDropboxCredentialService requires "App Key", "App Secret", "Access Token" and "Refresh Token". <br><br>
This document describes how to generate these credentials using an existing Dropbox account.
</p>
<h3>Generate App Key and App Secret</h3>
<p>
<ul>
<li> Login with your Dropbox account.</li>
<li>If you already have an app created, go to <a href="https://www.dropbox.com/developers">Dropbox Developers</a> page,
click on "App Console" button and select your app. On the app's info page you will find the "App key" and "App secret".<br>
(See also <a href="https://www.dropbox.com/developers/reference/getting-started"> Dropbox Getting Started</a>, App Console tab, "Navigating the App Console" chapter)</li>
<li> If you don't have any apps, go to <a href="https://www.dropbox.com/developers">Dropbox Developers</a> page and
click on "Create app" button. (See also <a href="https://www.dropbox.com/developers/reference/getting-started"> Dropbox Getting Started</a>, App Console tab,
"Creating a Dropbox app" chapter.)
<ul>
<li> On the next page select "Scoped access" and "Full Dropbox" as access type.</li>
<li> Provide a name for your app.</li>
<li>On the app's info page you will find the "App key" and "App secret".
(See also <a href="https://www.dropbox.com/developers/reference/getting-started">
Dropbox Getting Started</a>, App Console tab, "Navigating the App Console" chapter.)
</li>
</ul>
</li>
</ul>
</p>
<h3>Set required permissions for your app</h3>
<p>The "files.content.read" permission has to be enabled for the application to be able to read the files in Dropbox.<br><br>
You can set permissions in <a href="https://www.dropbox.com/developers">Dropbox Developers</a> page.
<li> Click on "App Console" button and select your app.</li>
<li> Go to "Permissions" tab and enable the "files.content.read" permission.</li>
<li> Click "Submit" button.</li>
<li> NOTE: In case you already have an Access Token and Refresh Token, those tokens have to be regenerated after the permission change.
See "Generate Access Token and Refresh Token" chapter about token generation.</li>
</p>
<h3>Generate Access Token and Refresh Token</h3>
<p>
<ul>
<li>Go to the following web page:
<pre>https://www.dropbox.com/oauth2/authorize?token_access_type=offline&response_type=code&client_id=<i>your_app_key</i></pre></li>
<li>Click "Next" and click on "Allow" button on the next page.</li>
<li>An access code will be generated for you, it will be displayed on the next page:</li>
<pre>
"Access Code Generated
Enter this code into your_app_name to finish the process
<i>your_generated_access_code</i>"
</pre>
<li>Execute the following command from terminal to fetch the access and refresh tokens. <br><br>
Make sure you execute the curl command right after the access code generation, since the code expires very quickly.<br>
In case the curl command returns "invalid grant" error, please generate a new access code (see previous step)<br>
<pre>curl https://api.dropbox.com/oauth2/token -d code=<i>your_generated_access_code</i> -d grant_type=authorization_code -u <i>your_app_key</i>:<i>your_app_secret</i></pre>
<br><br>
</li>
<li>The curl command results a json file which contains the "access_token" and "refresh_token":</li>
<code>
<pre>
{
"access_token": "sl.xxxxxxxxxxx"
"expires_in": 14400,
"refresh_token": "xxxxxx",
"scope": "files.content.read files.metadata.read",
"uid": "xxxxxx",
"account_id": "dbid:xxxx"
}
</pre>
</code>
</ul>
</p>
</body>
</html>

View File

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>nifi-nar-bundles</artifactId>
<groupId>org.apache.nifi</groupId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-dropbox-bundle</artifactId>
<version>1.18.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<dropbox.client.version>4.0.1</dropbox.client.version>
</properties>
<modules>
<module>nifi-dropbox-services-api</module>
<module>nifi-dropbox-services-api-nar</module>
<module>nifi-dropbox-processors</module>
<module>nifi-dropbox-processors-nar</module>
<module>nifi-dropbox-services</module>
<module>nifi-dropbox-services-nar</module>
</modules>
</project>

View File

@ -111,6 +111,7 @@
<module>nifi-salesforce-bundle</module>
<module>nifi-rocksdb-bundle</module>
<module>nifi-hubspot-bundle</module>
<module>nifi-dropbox-bundle</module>
</modules>
<build>