lex-sftp 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +16 -0
- data/.rspec +3 -0
- data/.rubocop.yml +69 -0
- data/CHANGELOG.md +18 -0
- data/CLAUDE.md +69 -0
- data/Gemfile +11 -0
- data/README.md +96 -0
- data/Rakefile +8 -0
- data/lex-sftp.gemspec +39 -0
- data/lib/legion/extensions/sftp/client.rb +33 -0
- data/lib/legion/extensions/sftp/helpers/connection.rb +39 -0
- data/lib/legion/extensions/sftp/runners/directory.rb +60 -0
- data/lib/legion/extensions/sftp/runners/file_ops.rb +62 -0
- data/lib/legion/extensions/sftp/runners/transfer.rb +68 -0
- data/lib/legion/extensions/sftp/version.rb +9 -0
- data/lib/legion/extensions/sftp.rb +16 -0
- metadata +174 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: cb95fa3bde3bd5117b1e879c99a749e932867f7f0a68bfd8d540dae22faa3ff1
|
|
4
|
+
data.tar.gz: 2b0d09032d24524acb461fa890c5efd93932859f490c30c342e7a1976f05774a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 246d939de6ab09f304acd886c67e508a2de3797dca21b5d30c472989698b1354ffc535e9ae058297f5d9188b12fe42fddf10e8731d411577026424134670ad66
|
|
7
|
+
data.tar.gz: a916a267c496fec2266994a35fdff12ae5f6755acbeb1ce42255dc19a264916d1c827898d018c739bc3a57a5294555a354d36181d0e882f7981c150f60777d07
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: ci
|
|
13
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.4
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
require:
|
|
7
|
+
- rubocop-rspec
|
|
8
|
+
|
|
9
|
+
Layout/LineLength:
|
|
10
|
+
Max: 160
|
|
11
|
+
|
|
12
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
13
|
+
EnforcedStyle: space
|
|
14
|
+
|
|
15
|
+
Layout/HashAlignment:
|
|
16
|
+
EnforcedHashRocketStyle: table
|
|
17
|
+
EnforcedColonStyle: table
|
|
18
|
+
|
|
19
|
+
Metrics/MethodLength:
|
|
20
|
+
Max: 50
|
|
21
|
+
|
|
22
|
+
Metrics/ClassLength:
|
|
23
|
+
Max: 1500
|
|
24
|
+
|
|
25
|
+
Metrics/ModuleLength:
|
|
26
|
+
Max: 1500
|
|
27
|
+
|
|
28
|
+
Metrics/BlockLength:
|
|
29
|
+
Max: 60
|
|
30
|
+
|
|
31
|
+
Metrics/AbcSize:
|
|
32
|
+
Max: 60
|
|
33
|
+
|
|
34
|
+
Metrics/CyclomaticComplexity:
|
|
35
|
+
Max: 15
|
|
36
|
+
|
|
37
|
+
Metrics/PerceivedComplexity:
|
|
38
|
+
Max: 17
|
|
39
|
+
|
|
40
|
+
Style/Documentation:
|
|
41
|
+
Enabled: false
|
|
42
|
+
|
|
43
|
+
Style/SymbolArray:
|
|
44
|
+
Enabled: true
|
|
45
|
+
|
|
46
|
+
Style/FrozenStringLiteralComment:
|
|
47
|
+
Enabled: true
|
|
48
|
+
EnforcedStyle: always
|
|
49
|
+
|
|
50
|
+
Naming/FileName:
|
|
51
|
+
Enabled: false
|
|
52
|
+
|
|
53
|
+
RSpec/MultipleExpectations:
|
|
54
|
+
Max: 5
|
|
55
|
+
|
|
56
|
+
RSpec/ExampleLength:
|
|
57
|
+
Max: 20
|
|
58
|
+
|
|
59
|
+
RSpec/VerifiedDoubles:
|
|
60
|
+
Enabled: false
|
|
61
|
+
|
|
62
|
+
RSpec/MessageSpies:
|
|
63
|
+
Enabled: false
|
|
64
|
+
|
|
65
|
+
RSpec/StubbedMock:
|
|
66
|
+
Enabled: false
|
|
67
|
+
|
|
68
|
+
RSpec/ContextWording:
|
|
69
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.2] - 2026-03-22
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- Add legion-cache, legion-crypt, legion-data, legion-json, legion-logging, legion-settings, legion-transport as runtime dependencies
|
|
7
|
+
- Update spec_helper with real sub-gem helper stubs
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-03-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Initial release
|
|
13
|
+
- `Helpers::Connection` — Net::SFTP session factory with password, key_path, key_data auth
|
|
14
|
+
- `Runners::FileOps` — upload, download, delete, rename, stat
|
|
15
|
+
- `Runners::Directory` — list, mkdir, rmdir, entries (with glob pattern filter)
|
|
16
|
+
- `Runners::Transfer` — batch_upload, batch_download, sync (upload/download directions)
|
|
17
|
+
- Standalone `Client` class including all runner modules
|
|
18
|
+
- 46 specs, 0 failures
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# lex-sftp: SFTP File Transfer Extension for LegionIO
|
|
2
|
+
|
|
3
|
+
**Repository Level 3 Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-other/CLAUDE.md`
|
|
5
|
+
- **Grandparent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Legion Extension for SFTP file operations. Designed for healthcare file exchange (HL7, EDI, FHIR bundles). Provides runners for uploading, downloading, and managing files on SFTP servers via Net::SFTP.
|
|
10
|
+
|
|
11
|
+
**Version**: 0.1.0
|
|
12
|
+
**GitHub**: https://github.com/LegionIO/lex-sftp
|
|
13
|
+
**License**: MIT
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Legion::Extensions::Sftp
|
|
19
|
+
├── Runners/
|
|
20
|
+
│ ├── FileOps # upload, download, delete, rename, stat
|
|
21
|
+
│ ├── Directory # list, mkdir, rmdir, entries (glob pattern filter)
|
|
22
|
+
│ └── Transfer # batch_upload, batch_download, sync (upload/download)
|
|
23
|
+
├── Helpers/
|
|
24
|
+
│ └── Connection # Net::SFTP session factory (password, key_path, key_data auth)
|
|
25
|
+
└── Client # Standalone client class (includes all runners)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Key Files
|
|
29
|
+
|
|
30
|
+
| Path | Purpose |
|
|
31
|
+
|------|---------|
|
|
32
|
+
| `lib/legion/extensions/sftp.rb` | Entry point, extension registration |
|
|
33
|
+
| `lib/legion/extensions/sftp/runners/file_ops.rb` | upload, download, delete, rename, stat |
|
|
34
|
+
| `lib/legion/extensions/sftp/runners/directory.rb` | list, mkdir, rmdir, entries |
|
|
35
|
+
| `lib/legion/extensions/sftp/runners/transfer.rb` | batch_upload, batch_download, sync |
|
|
36
|
+
| `lib/legion/extensions/sftp/helpers/connection.rb` | `connection(**opts)` — Net::SFTP.start wrapper |
|
|
37
|
+
| `lib/legion/extensions/sftp/client.rb` | Standalone `Client` class |
|
|
38
|
+
|
|
39
|
+
## Connection Options
|
|
40
|
+
|
|
41
|
+
| Key | Default | Description |
|
|
42
|
+
|-----|---------|-------------|
|
|
43
|
+
| `host` | settings or required | SFTP server hostname |
|
|
44
|
+
| `user` | `'sftp'` | SFTP username |
|
|
45
|
+
| `port` | `22` | SFTP port |
|
|
46
|
+
| `password` | nil | Password auth |
|
|
47
|
+
| `key_path` | nil | Private key file path |
|
|
48
|
+
| `key_data` | nil | Inline private key data |
|
|
49
|
+
| `timeout` | `30` | Connection timeout (seconds) |
|
|
50
|
+
|
|
51
|
+
## Dependencies
|
|
52
|
+
|
|
53
|
+
| Gem | Purpose |
|
|
54
|
+
|-----|---------|
|
|
55
|
+
| `net-sftp` (~> 4.0) | SFTP client (built on net-ssh) |
|
|
56
|
+
|
|
57
|
+
## Development
|
|
58
|
+
|
|
59
|
+
46 specs total.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bundle install
|
|
63
|
+
bundle exec rspec
|
|
64
|
+
bundle exec rubocop
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
**Maintained By**: Matthew Iverson (@Esity)
|
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# lex-sftp
|
|
2
|
+
|
|
3
|
+
SFTP file transfer for [LegionIO](https://github.com/LegionIO/LegionIO). Upload, download, and manage files on SFTP servers from within task chains. Designed for healthcare file exchange (HL7, EDI, FHIR bundles).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gem install lex-sftp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or add to your Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'lex-sftp'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Runners
|
|
18
|
+
|
|
19
|
+
### FileOps
|
|
20
|
+
|
|
21
|
+
| Method | Parameters | Description |
|
|
22
|
+
|--------|-----------|-------------|
|
|
23
|
+
| `upload` | `local_path:`, `remote_path:`, `**opts` | Upload a file to the SFTP server |
|
|
24
|
+
| `download` | `remote_path:`, `local_path:`, `**opts` | Download a file from the SFTP server |
|
|
25
|
+
| `delete` | `remote_path:`, `**opts` | Remove a file from the SFTP server |
|
|
26
|
+
| `rename` | `old_path:`, `new_path:`, `**opts` | Rename or move a remote file |
|
|
27
|
+
| `stat` | `remote_path:`, `**opts` | Get file attributes (size, mtime, permissions) |
|
|
28
|
+
|
|
29
|
+
All methods return `{ success: true/false, ... }` hashes.
|
|
30
|
+
|
|
31
|
+
### Directory
|
|
32
|
+
|
|
33
|
+
| Method | Parameters | Description |
|
|
34
|
+
|--------|-----------|-------------|
|
|
35
|
+
| `list` | `remote_path:`, `**opts` | List directory entries |
|
|
36
|
+
| `mkdir` | `remote_path:`, `**opts` | Create a remote directory |
|
|
37
|
+
| `rmdir` | `remote_path:`, `**opts` | Remove a remote directory |
|
|
38
|
+
| `entries` | `remote_path:`, `pattern: nil`, `**opts` | Filtered directory listing (glob pattern) |
|
|
39
|
+
|
|
40
|
+
### Transfer
|
|
41
|
+
|
|
42
|
+
| Method | Parameters | Description |
|
|
43
|
+
|--------|-----------|-------------|
|
|
44
|
+
| `batch_upload` | `files:`, `remote_dir:`, `**opts` | Upload an array of local file paths |
|
|
45
|
+
| `batch_download` | `files:`, `local_dir:`, `**opts` | Download an array of remote file paths |
|
|
46
|
+
| `sync` | `local_dir:`, `remote_dir:`, `direction: :upload`, `**opts` | Sync a directory (`:upload` or `:download`) |
|
|
47
|
+
|
|
48
|
+
## Authentication
|
|
49
|
+
|
|
50
|
+
Pass authentication kwargs alongside other parameters:
|
|
51
|
+
|
|
52
|
+
| Parameter | Description |
|
|
53
|
+
|-----------|-------------|
|
|
54
|
+
| `host` | SFTP server hostname or IP |
|
|
55
|
+
| `user` | SFTP username (default: `'sftp'`) |
|
|
56
|
+
| `port` | SFTP port (default: `22`) |
|
|
57
|
+
| `password` | Password authentication |
|
|
58
|
+
| `key_path` | Path to private key file |
|
|
59
|
+
| `key_data` | Inline private key data |
|
|
60
|
+
| `timeout` | Connection timeout in seconds (default: `30`) |
|
|
61
|
+
|
|
62
|
+
## Standalone Client
|
|
63
|
+
|
|
64
|
+
Use `Legion::Extensions::Sftp::Client` outside the full LegionIO framework.
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
require 'legion/extensions/sftp'
|
|
68
|
+
|
|
69
|
+
client = Legion::Extensions::Sftp::Client.new(
|
|
70
|
+
host: 'sftp.clearinghouse.example.com',
|
|
71
|
+
user: 'hl7_sender',
|
|
72
|
+
key_path: '/etc/legion/sftp_key'
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Upload HL7 message
|
|
76
|
+
client.upload(local_path: '/tmp/ADT_A01_20240101.hl7', remote_path: '/inbound/ADT_A01_20240101.hl7')
|
|
77
|
+
|
|
78
|
+
# List outbound results
|
|
79
|
+
result = client.entries(remote_path: '/outbound', pattern: '*.hl7')
|
|
80
|
+
result[:entries].each { |e| puts e[:name] }
|
|
81
|
+
|
|
82
|
+
# Download all results
|
|
83
|
+
client.sync(local_dir: '/tmp/results', remote_dir: '/outbound', direction: :download)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Constructor: `Client.new(**opts)`. All kwargs are stored and passed through to Net::SFTP on each call.
|
|
87
|
+
|
|
88
|
+
## Requirements
|
|
89
|
+
|
|
90
|
+
- Ruby >= 3.4
|
|
91
|
+
- SFTP server access
|
|
92
|
+
- `net-sftp` ~> 4.0
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT
|
data/Rakefile
ADDED
data/lex-sftp.gemspec
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/sftp/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-sftp'
|
|
7
|
+
spec.version = Legion::Extensions::Sftp::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX::SFTP'
|
|
12
|
+
spec.description = 'LEX::SFTP — SFTP file transfer for LegionIO (healthcare HL7, EDI, FHIR)'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-sftp'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-sftp'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-sftp'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-sftp'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-sftp/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
spec.require_paths = ['lib']
|
|
29
|
+
|
|
30
|
+
spec.add_dependency 'net-sftp', '~> 4.0'
|
|
31
|
+
|
|
32
|
+
spec.add_dependency 'legion-cache', '>= 1.3.11'
|
|
33
|
+
spec.add_dependency 'legion-crypt', '>= 1.4.9'
|
|
34
|
+
spec.add_dependency 'legion-data', '>= 1.4.17'
|
|
35
|
+
spec.add_dependency 'legion-json', '>= 1.2.1'
|
|
36
|
+
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
37
|
+
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
38
|
+
spec.add_dependency 'legion-transport', '>= 1.3.9'
|
|
39
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helpers/connection'
|
|
4
|
+
require_relative 'runners/file_ops'
|
|
5
|
+
require_relative 'runners/directory'
|
|
6
|
+
require_relative 'runners/transfer'
|
|
7
|
+
|
|
8
|
+
module Legion
|
|
9
|
+
module Extensions
|
|
10
|
+
module Sftp
|
|
11
|
+
class Client
|
|
12
|
+
include Helpers::Connection
|
|
13
|
+
include Runners::FileOps
|
|
14
|
+
include Runners::Directory
|
|
15
|
+
include Runners::Transfer
|
|
16
|
+
|
|
17
|
+
attr_reader :opts
|
|
18
|
+
|
|
19
|
+
def initialize(**opts)
|
|
20
|
+
@opts = opts
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def settings
|
|
24
|
+
{ options: @opts }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def connection(**override)
|
|
28
|
+
super(**@opts, **override)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/sftp'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Sftp
|
|
8
|
+
module Helpers
|
|
9
|
+
module Connection
|
|
10
|
+
def connection(host: nil, user: 'sftp', port: 22, **)
|
|
11
|
+
host ||= settings_dig(:host)
|
|
12
|
+
user = settings_dig(:user) || user if user == 'sftp'
|
|
13
|
+
conn_opts = build_connection_opts(port: port, **)
|
|
14
|
+
Net::SFTP.start(host, user, **conn_opts)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def build_connection_opts(port: 22, timeout: 30, **rest)
|
|
20
|
+
opts = { port: port, timeout: timeout, non_interactive: true }
|
|
21
|
+
opts[:password] = rest[:password] if rest[:password]
|
|
22
|
+
opts[:keys] = [rest[:key_path]] if rest[:key_path]
|
|
23
|
+
opts[:key_data] = [rest[:key_data]] if rest[:key_data]
|
|
24
|
+
opts
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def settings_dig(key)
|
|
28
|
+
return unless defined?(Legion::Settings)
|
|
29
|
+
|
|
30
|
+
Legion::Settings.dig(:sftp, key)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers) &&
|
|
34
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Sftp
|
|
6
|
+
module Runners
|
|
7
|
+
module Directory
|
|
8
|
+
include Legion::Extensions::Sftp::Helpers::Connection
|
|
9
|
+
|
|
10
|
+
def list(remote_path:, **)
|
|
11
|
+
sftp = connection(**)
|
|
12
|
+
entries = sftp.dir.entries(remote_path).map do |entry|
|
|
13
|
+
{
|
|
14
|
+
name: entry.name,
|
|
15
|
+
longname: entry.longname,
|
|
16
|
+
size: entry.attributes.size,
|
|
17
|
+
mtime: entry.attributes.mtime,
|
|
18
|
+
permissions: entry.attributes.permissions
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
{ success: true, remote_path: remote_path, entries: entries, count: entries.size }
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
{ success: false, remote_path: remote_path, error: e.message }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def mkdir(remote_path:, **)
|
|
27
|
+
sftp = connection(**)
|
|
28
|
+
sftp.mkdir!(remote_path)
|
|
29
|
+
{ success: true, remote_path: remote_path }
|
|
30
|
+
rescue StandardError => e
|
|
31
|
+
{ success: false, remote_path: remote_path, error: e.message }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def rmdir(remote_path:, **)
|
|
35
|
+
sftp = connection(**)
|
|
36
|
+
sftp.rmdir!(remote_path)
|
|
37
|
+
{ success: true, remote_path: remote_path }
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
{ success: false, remote_path: remote_path, error: e.message }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def entries(remote_path:, pattern: nil, **)
|
|
43
|
+
result = list(remote_path: remote_path, **)
|
|
44
|
+
return result unless result[:success]
|
|
45
|
+
|
|
46
|
+
filtered = if pattern
|
|
47
|
+
result[:entries].select { |e| File.fnmatch(pattern, e[:name]) }
|
|
48
|
+
else
|
|
49
|
+
result[:entries]
|
|
50
|
+
end
|
|
51
|
+
{ success: true, remote_path: remote_path, entries: filtered, count: filtered.size, pattern: pattern }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers) &&
|
|
55
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Sftp
|
|
6
|
+
module Runners
|
|
7
|
+
module FileOps
|
|
8
|
+
include Legion::Extensions::Sftp::Helpers::Connection
|
|
9
|
+
|
|
10
|
+
def upload(local_path:, remote_path:, **)
|
|
11
|
+
sftp = connection(**)
|
|
12
|
+
sftp.upload!(local_path, remote_path)
|
|
13
|
+
{ success: true, local_path: local_path, remote_path: remote_path }
|
|
14
|
+
rescue StandardError => e
|
|
15
|
+
{ success: false, local_path: local_path, remote_path: remote_path, error: e.message }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def download(remote_path:, local_path:, **)
|
|
19
|
+
sftp = connection(**)
|
|
20
|
+
sftp.download!(remote_path, local_path)
|
|
21
|
+
{ success: true, remote_path: remote_path, local_path: local_path }
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
{ success: false, remote_path: remote_path, local_path: local_path, error: e.message }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete(remote_path:, **)
|
|
27
|
+
sftp = connection(**)
|
|
28
|
+
sftp.remove!(remote_path)
|
|
29
|
+
{ success: true, remote_path: remote_path }
|
|
30
|
+
rescue StandardError => e
|
|
31
|
+
{ success: false, remote_path: remote_path, error: e.message }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def rename(old_path:, new_path:, **)
|
|
35
|
+
sftp = connection(**)
|
|
36
|
+
sftp.rename!(old_path, new_path)
|
|
37
|
+
{ success: true, old_path: old_path, new_path: new_path }
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
{ success: false, old_path: old_path, new_path: new_path, error: e.message }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def stat(remote_path:, **)
|
|
43
|
+
sftp = connection(**)
|
|
44
|
+
attrs = sftp.stat!(remote_path)
|
|
45
|
+
{
|
|
46
|
+
success: true,
|
|
47
|
+
remote_path: remote_path,
|
|
48
|
+
size: attrs.size,
|
|
49
|
+
mtime: attrs.mtime,
|
|
50
|
+
permissions: attrs.permissions
|
|
51
|
+
}
|
|
52
|
+
rescue StandardError => e
|
|
53
|
+
{ success: false, remote_path: remote_path, error: e.message }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers) &&
|
|
57
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Sftp
|
|
6
|
+
module Runners
|
|
7
|
+
module Transfer
|
|
8
|
+
include Legion::Extensions::Sftp::Helpers::Connection
|
|
9
|
+
|
|
10
|
+
def batch_upload(files:, remote_dir:, **)
|
|
11
|
+
results = files.map do |local_path|
|
|
12
|
+
filename = File.basename(local_path)
|
|
13
|
+
remote_path = "#{remote_dir}/#{filename}"
|
|
14
|
+
upload(local_path: local_path, remote_path: remote_path, **)
|
|
15
|
+
end
|
|
16
|
+
succeeded = results.count { |r| r[:success] }
|
|
17
|
+
failed = results.count { |r| !r[:success] }
|
|
18
|
+
{ success: failed.zero?, results: results, succeeded: succeeded, failed: failed, total: files.size }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def batch_download(files:, local_dir:, **)
|
|
22
|
+
results = files.map do |remote_path|
|
|
23
|
+
filename = File.basename(remote_path)
|
|
24
|
+
local_path = File.join(local_dir, filename)
|
|
25
|
+
download(remote_path: remote_path, local_path: local_path, **)
|
|
26
|
+
end
|
|
27
|
+
succeeded = results.count { |r| r[:success] }
|
|
28
|
+
failed = results.count { |r| !r[:success] }
|
|
29
|
+
{ success: failed.zero?, results: results, succeeded: succeeded, failed: failed, total: files.size }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def sync(local_dir:, remote_dir:, direction: :upload, **)
|
|
33
|
+
case direction.to_sym
|
|
34
|
+
when :upload
|
|
35
|
+
sync_upload(local_dir: local_dir, remote_dir: remote_dir, **)
|
|
36
|
+
when :download
|
|
37
|
+
sync_download(local_dir: local_dir, remote_dir: remote_dir, **)
|
|
38
|
+
else
|
|
39
|
+
{ success: false, error: "unknown direction: #{direction}" }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def sync_upload(local_dir:, remote_dir:, **)
|
|
46
|
+
files = Dir.glob(File.join(local_dir, '*')).select { |f| File.file?(f) }
|
|
47
|
+
result = batch_upload(files: files, remote_dir: remote_dir, **)
|
|
48
|
+
result.merge(direction: :upload, local_dir: local_dir, remote_dir: remote_dir)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def sync_download(local_dir:, remote_dir:, **)
|
|
52
|
+
dir_result = list(remote_path: remote_dir, **)
|
|
53
|
+
return dir_result unless dir_result[:success]
|
|
54
|
+
|
|
55
|
+
remote_files = dir_result[:entries]
|
|
56
|
+
.reject { |e| e[:name].start_with?('.') }
|
|
57
|
+
.map { |e| "#{remote_dir}/#{e[:name]}" }
|
|
58
|
+
result = batch_download(files: remote_files, local_dir: local_dir, **)
|
|
59
|
+
result.merge(direction: :download, local_dir: local_dir, remote_dir: remote_dir)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers) &&
|
|
63
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/sftp/version'
|
|
4
|
+
require 'legion/extensions/sftp/helpers/connection'
|
|
5
|
+
require 'legion/extensions/sftp/runners/file_ops'
|
|
6
|
+
require 'legion/extensions/sftp/runners/directory'
|
|
7
|
+
require 'legion/extensions/sftp/runners/transfer'
|
|
8
|
+
require 'legion/extensions/sftp/client'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module Sftp
|
|
13
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-sftp
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: net-sftp
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '4.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '4.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: legion-cache
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.3.11
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 1.3.11
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: legion-crypt
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 1.4.9
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 1.4.9
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: legion-data
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 1.4.17
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 1.4.17
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: legion-json
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 1.2.1
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.2.1
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: legion-logging
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 1.3.2
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 1.3.2
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: legion-settings
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 1.3.14
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 1.3.14
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: legion-transport
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 1.3.9
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: 1.3.9
|
|
124
|
+
description: LEX::SFTP — SFTP file transfer for LegionIO (healthcare HL7, EDI, FHIR)
|
|
125
|
+
email:
|
|
126
|
+
- matthewdiverson@gmail.com
|
|
127
|
+
executables: []
|
|
128
|
+
extensions: []
|
|
129
|
+
extra_rdoc_files: []
|
|
130
|
+
files:
|
|
131
|
+
- ".github/workflows/ci.yml"
|
|
132
|
+
- ".rspec"
|
|
133
|
+
- ".rubocop.yml"
|
|
134
|
+
- CHANGELOG.md
|
|
135
|
+
- CLAUDE.md
|
|
136
|
+
- Gemfile
|
|
137
|
+
- README.md
|
|
138
|
+
- Rakefile
|
|
139
|
+
- lex-sftp.gemspec
|
|
140
|
+
- lib/legion/extensions/sftp.rb
|
|
141
|
+
- lib/legion/extensions/sftp/client.rb
|
|
142
|
+
- lib/legion/extensions/sftp/helpers/connection.rb
|
|
143
|
+
- lib/legion/extensions/sftp/runners/directory.rb
|
|
144
|
+
- lib/legion/extensions/sftp/runners/file_ops.rb
|
|
145
|
+
- lib/legion/extensions/sftp/runners/transfer.rb
|
|
146
|
+
- lib/legion/extensions/sftp/version.rb
|
|
147
|
+
homepage: https://github.com/LegionIO/lex-sftp
|
|
148
|
+
licenses:
|
|
149
|
+
- MIT
|
|
150
|
+
metadata:
|
|
151
|
+
homepage_uri: https://github.com/LegionIO/lex-sftp
|
|
152
|
+
source_code_uri: https://github.com/LegionIO/lex-sftp
|
|
153
|
+
documentation_uri: https://github.com/LegionIO/lex-sftp
|
|
154
|
+
changelog_uri: https://github.com/LegionIO/lex-sftp
|
|
155
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-sftp/issues
|
|
156
|
+
rubygems_mfa_required: 'true'
|
|
157
|
+
rdoc_options: []
|
|
158
|
+
require_paths:
|
|
159
|
+
- lib
|
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
161
|
+
requirements:
|
|
162
|
+
- - ">="
|
|
163
|
+
- !ruby/object:Gem::Version
|
|
164
|
+
version: '3.4'
|
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
|
+
requirements:
|
|
167
|
+
- - ">="
|
|
168
|
+
- !ruby/object:Gem::Version
|
|
169
|
+
version: '0'
|
|
170
|
+
requirements: []
|
|
171
|
+
rubygems_version: 3.6.9
|
|
172
|
+
specification_version: 4
|
|
173
|
+
summary: LEX::SFTP
|
|
174
|
+
test_files: []
|