Skip to content

Commit f88c663

Browse files
committed
added the FtpDataProvider module, updated the FileDataProvider module
1 parent e3402e5 commit f88c663

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1987
-55
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@ qore_user_module("qlib/TextWrap.qm" "Util")
849849
# base 3 modules
850850
qore_user_module("qlib/FilePoller.qm" "Util;DataProvider")
851851
qore_user_module("qlib/FileDataProvider" "FsUtil;DataProvider")
852+
qore_user_module("qlib/FtpDataProvider" "DataProvider")
852853
qore_user_module("qlib/SqlUtil" "Util;DataProvider")
853854
qore_user_module("qlib/ConnectionProvider" "Util;DataProvider")
854855
qore_user_module("qlib/CsvUtil" "Util;DataProvider")

Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ DOX_SPLIT_MODULES = \
8282
qlib/SalesforceRestDataProvider \
8383
qlib/CdsRestDataProvider \
8484
qlib/ServiceNowRestDataProvider \
85-
qlib/FileDataProvider
85+
qlib/FileDataProvider \
86+
qlib/FtpDataProvider
8687

8788
SqlUtil_modverdir = $(modverdir)/SqlUtil
8889
ConnectionProvider_modverdir = $(modverdir)/ConnectionProvider

bin/qdp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ class InputHelper {
821821
Linenoise::set_callback(\completion());
822822

823823
try {
824-
Linenoise::history_load(HistoryFile);
824+
Linenoise::history_load(ENV.HOME + "/" + HistoryFile);
825825
} catch (hash<ExceptionInfo> ex) {
826826
printf("cannot load history: %s: %s\n", ex.err, ex.desc);
827827
}

doxygen/lang/120_modules.dox.tmpl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ ModuleName/
8686
management, programmatic DB access, schema and data synchronization, and more
8787
|user|<a href="../../modules/FsUtil/html/index.html">FsUtil</a>|Provides a high level API for working with the \
8888
filesystem
89+
|user|<a href="../../modules/FtpDataProvider/html/index.html">FtpDataProvider</a>|Provides a \
90+
<a href="../../modules/DataProvider/html/index.html">data provider</a> API for the FTP protocol
8991
|user|<a href="../../modules/FtpPoller/html/index.html">FtpPoller</a>|Provides an API for polling files from a \
9092
remote directory with the FTP protocol
9193
|user|<a href="../../modules/FtpPollerUtil/html/index.html">FtpPollerUtil</a>|Provides definitions for the \

doxygen/lang/900_release_notes.dox.tmpl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
(<a href="https://github.com/qorelanguage/qore/issues/4557">issue 4557</a>)
1414
- <a href="../../modules/FileDataProvider/html/index.html">FileDataProvider</a> module
1515
- new module
16+
- <a href="../../modules/FtpDataProvider/html/index.html">FtpDataProvider</a> module
17+
- new module
1618
- <a href="../../modules/FtpPoller/html/index.html">FtpPoller</a> module
1719
- added support for the event-based DataProvider API
1820
(<a href="https://github.com/qorelanguage/qore/issues/4557">issue 4557</a>)

examples/test/qlib/FileDataProvider/FileDataProvider.qtest

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,27 @@ public class FileDataProviderTest inherits QUnit::Test {
6565
});
6666
assertEq(new_targ, h.path);
6767
assertFalse(is_file(new_targ));
68+
69+
prov = fdp.getChildProviderEx("create");
70+
h = prov.doRequest({
71+
"path": new_targ,
72+
"data": str,
73+
});
74+
assertEq(new_targ, h.path);
75+
assertTrue(is_file(new_targ));
76+
77+
prov = fdp.getChildProviderEx("get");
78+
h = prov.doRequest({
79+
"path": new_targ,
80+
"text": True,
81+
});
82+
assertEq(new_targ, h.path);
83+
assertEq(str, h."data");
84+
85+
prov = fdp.getChildProviderEx("list");
86+
h = prov.doRequest({
87+
"path": dirname(tmp.path),
88+
})[0];
89+
assertEq(basename(new_targ), h.name);
6890
}
6991
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env qore
2+
# -*- mode: qore; indent-tabs-mode: nil -*-
3+
4+
%require-types
5+
%enable-all-warnings
6+
%new-style
7+
%strict-args
8+
%allow-injection
9+
10+
%requires ../../../../qlib/Util.qm
11+
%requires ../../../../qlib/QUnit.qm
12+
%requires ../../../../qlib/Logger.qm
13+
%requires ../../../../qlib/DataProvider
14+
%requires ../../../../qlib/FsUtil.qm
15+
%requires ../../../../qlib/FtpDataProvider
16+
17+
%include ../../qore/classes/FtpServer.qc
18+
19+
%exec-class FtpDataProviderTest
20+
21+
public class FtpDataProviderTest inherits QUnit::Test {
22+
constructor() : Test("FtpDataProvider Test", "1.0") {
23+
addTestCase("test", \ftpDataProviderTest());
24+
25+
# Return for compatibility with test harness that checks return value.
26+
set_return_value(main());
27+
}
28+
29+
ftpDataProviderTest() {
30+
int port = getPort();
31+
32+
# PORT only supports IPv4, so we can't use "localhost", which may resolve
33+
# to an IPv6 address
34+
FtpServer serv(port, m_options.verbose - 1, "127.0.0.1");
35+
on_exit serv.shutdown();
36+
37+
serv.setFs();
38+
serv.setDirect();
39+
40+
port = serv.getPort();
41+
string url = sprintf("ftp://user:pass@127.0.0.1:%d", port);
42+
43+
FtpDataProvider root({"url": url});
44+
AbstractDataProvider prov = root.getChildProviderEx("create-file");
45+
string str = get_random_string();
46+
string path = "/test.txt";
47+
hash<auto> h = prov.doRequest({
48+
"path": path,
49+
"data": str,
50+
});
51+
assertEq(path, h.path);
52+
53+
prov = root.getChildProviderEx("get-file");
54+
h = prov.doRequest({
55+
"path": path,
56+
"text": True,
57+
});
58+
assertEq(str, h."data");
59+
60+
prov = root.getChildProviderEx("stat");
61+
h = prov.doRequest({
62+
"path": path,
63+
});
64+
assertEq({
65+
"name": "test.txt",
66+
"path": "/test.txt",
67+
"type": "REGULAR",
68+
"size": 15,
69+
}, h - "mtime");
70+
71+
/* test server doesn't support RNFR yet
72+
string newpath = "/test1.txt";
73+
prov = root.getChildProviderEx("move");
74+
h = prov.doRequest({
75+
"source": path,
76+
"target": newpath,
77+
});
78+
assertEq(newpath, h.path);
79+
path = newpath;
80+
*/
81+
82+
prov = root.getChildProviderEx("delete");
83+
h = prov.doRequest({
84+
"path": path,
85+
});
86+
assertEq(path, h.path);
87+
88+
prov = root.getChildProviderEx("stat");
89+
assertThrows("FTP-STAT-ERROR", \prov.doRequest(), {
90+
"path": path,
91+
});
92+
}
93+
94+
private softint getPort() {
95+
return m_options.port ?? ENV.FTPCLIENT_TEST_PORT ?? 0;
96+
}
97+
}

examples/test/qore/classes/FtpServer.qc

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -288,33 +288,65 @@ class FtpServer {
288288
}
289289

290290
if (cmd =~ /^CWD/i) {
291-
string path = (cmd =~ x/^CWD (.+)$/i)[0];
292-
pwd = path;
293-
ftpSend(ns, 250, "Okay.");
291+
string dir = (cmd =~ x/^CWD (.+)$/i)[0];
292+
293+
*string path = t ? t.path : pwd;
294+
path += DirSep + dir;
295+
296+
if (!is_dir(path)) {
297+
ftpSend(ns, 550, "Failed to change directory to " + dir);
298+
} else {
299+
ftpSend(ns, 250, "Okay.");
300+
pwd = dir;
301+
}
294302
continue;
295303
}
296304

297305
if (cmd =~ /^MDTM/i) {
298306
string file_name = (cmd =~ x/^MDTM (.+)$/i)[0];
299307
# printf("FILE_NAME: %y\n", file_name);
300308

301-
Dir dir();
302-
dir.chdir(pwd);
303-
auto file = dir.list(file_name, 0, True)[0];
309+
*string path = t ? t.path : pwd;
310+
path += DirSep + file_name;
304311

305-
ftpSend(ns, 250, file.mtime.toString());
312+
*hash<StatInfo> info = hstat(path);
313+
if (!info) {
314+
ftpSend(ns, 550, "Failed to retrieve modified date for " + file_name);
315+
} else {
316+
ftpSend(ns, 250, info.mtime.toString());
317+
}
306318
continue;
307319
}
308320

309321
if (cmd =~ /^SIZE/i) {
310322
string file_name = (cmd =~ x/^SIZE (.+)$/i)[0];
311323
# printf("FILE_NAME: %y\n", file_name);
312324

313-
Dir dir();
314-
dir.chdir(pwd);
315-
auto file = dir.list(file_name, 0, True)[0];
325+
*string path = t ? t.path : pwd;
326+
path += DirSep + file_name;
327+
328+
*hash<StatInfo> info = hstat(path);
329+
if (!info) {
330+
ftpSend(ns, 550, "Failed to retrieve size for " + file_name);
331+
} else {
332+
ftpSend(ns, 250, info.size.toString());
333+
}
334+
continue;
335+
}
336+
337+
if (cmd =~ /^DELE/i) {
338+
string file_name = (cmd =~ x/^DELE (.+)$/i)[0];
339+
# printf("FILE_NAME: %y\n", file_name);
340+
341+
*string path = t ? t.path : pwd;
342+
path += DirSep + file_name;
316343

317-
ftpSend(ns, 250, file.size.toString());
344+
int rc = unlink(path);
345+
if (rc) {
346+
ftpSend(ns, 550, "Failed to delete " + file_name);
347+
} else {
348+
ftpSend(ns, 250, "Okay.");
349+
}
318350
continue;
319351
}
320352

lib/QC_FtpClient.qpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ nothing FtpClient::connect() {
176176
177177
@par Example:
178178
@code{.py}
179-
delete ftp;
179+
ftp.disconnect();
180180
@endcode
181181
*/
182182
nothing FtpClient::disconnect() {

lib/ql_file.qpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,8 +683,9 @@ nothing unlink() [flags=RUNTIME_NOOP;dom=FILESYSTEM] {
683683
684684
@par Example:
685685
@code{.py}
686-
if (unlink(path))
686+
if (umask(mask)) {
687687
printf("%s: %s\n", path, strerror());
688+
}
688689
@endcode
689690
*/
690691
int umask(softint mask) [dom=PROCESS] {

qlib/DataProvider/DataProvider.qc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class DataProvider {
4040
"filepoller": "FilePoller",
4141
"fixedlengthread": "FixedLengthUtil",
4242
"fixedlengthwrite": "FixedLengthUtil",
43+
"ftp": "FtpDataProvider",
4344
"ftppoller": "FtpPoller",
4445
"salesforcerest": "SalesforceRestDataProvider",
4546
"servicenowrest": "ServiceNowRestDataProvider",

qlib/FileDataProvider/FileCopyDataProvider.qc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- mode: qore; indent-tabs-mode: nil -*-
2-
#! Qore FileCopyDataProvider module definition
2+
#! Qore FileDataProvider module definition
33

4-
/** FileCopyDataProvider.qc Copyright 2019 - 2022 Qore Technologies, s.r.o.
4+
/** FileCopyDataProvider.qc Copyright 2022 Qore Technologies, s.r.o.
55

66
Permission is hereby granted, free of charge, to any person obtaining a
77
copy of this software and associated documentation files (the "Software"),
@@ -22,9 +22,9 @@
2222
DEALINGS IN THE SOFTWARE.
2323
*/
2424

25-
#! contains all public definitions in the FileCopyDataProvider module
25+
#! contains all public definitions in the FileDataProvider module
2626
public namespace FileDataProvider {
27-
#! The File data provider class
27+
#! The File copy data provider class
2828
public class FileCopyDataProvider inherits AbstractDataProvider {
2929
public {
3030
#! Provider info

qlib/FileDataProvider/FileCopyRequestDataType.qc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
DEALINGS IN THE SOFTWARE.
2222
*/
2323

24-
#! contains all public definitions in the FileCopyDataProvider module
24+
#! contains all public definitions in the FileDataProvider module
2525
public namespace FileDataProvider {
2626
#! Data type for copy file request calls
2727
public class FileCopyRequestDataType inherits FileMoveRequestDataType {
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# -*- mode: qore; indent-tabs-mode: nil -*-
2+
#! Qore FileDataProvider module definition
3+
4+
/** FileCreateDataProvider.qc Copyright 2022 Qore Technologies, s.r.o.
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a
7+
copy of this software and associated documentation files (the "Software"),
8+
to deal in the Software without restriction, including without limitation
9+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+
and/or sell copies of the Software, and to permit persons to whom the
11+
Software is furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22+
DEALINGS IN THE SOFTWARE.
23+
*/
24+
25+
#! contains all public definitions in the FileDataProvider module
26+
public namespace FileDataProvider {
27+
#! The create file data provider class
28+
public class FileCreateDataProvider inherits AbstractDataProvider {
29+
public {
30+
#! Provider info
31+
const ProviderInfo = <DataProviderInfo>{
32+
"type": "FileCreateDataProvider",
33+
"supports_request": True,
34+
};
35+
36+
#! Request type
37+
const RequestType = new FileCreateRequestDataType();
38+
39+
#! Response type
40+
const ResponseType = new FilePathDataType();
41+
}
42+
43+
#! Creates the object from constructor options
44+
constructor(*hash<auto> options) {
45+
checkOptions("CONSTRUCTOR-ERROR", NOTHING, options);
46+
}
47+
48+
#! Returns the data provider name
49+
string getName() {
50+
return "create";
51+
}
52+
53+
#! Returns the data provider description
54+
*string getDesc() {
55+
return "Create file data provider; creates a file in the given location";
56+
}
57+
58+
#! Makes a request and returns the response
59+
/** @param req the request info
60+
@param request_options the request options after processing by validateRequestOptions()
61+
62+
@return the response to the request
63+
*/
64+
private auto doRequestImpl(auto req, *hash<auto> request_options) {
65+
File f();
66+
f.open2(req.path, O_CREAT | O_TRUNC | O_WRONLY, req.mode);
67+
f.write(req."data");
68+
return {
69+
"path": req.path,
70+
};
71+
}
72+
73+
#! Returns the description of a successful request message, if any
74+
/** @return the request type for this provider
75+
*/
76+
private *AbstractDataProviderType getRequestTypeImpl() {
77+
return RequestType;
78+
}
79+
80+
#! Returns the description of a response message, if this object represents a response message
81+
/** @return the response type for this response message
82+
*/
83+
private *AbstractDataProviderType getResponseTypeImpl() {
84+
return ResponseType;
85+
}
86+
87+
#! Returns data provider static info
88+
hash<DataProviderInfo> getStaticInfoImpl() {
89+
return ProviderInfo;
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)