mirror of
https://github.com/captn3m0/outliner.git
synced 2024-09-28 22:23:03 +00:00
Compare commits
No commits in common. "master" and "v1.0.0" have entirely different histories.
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@ -1,3 +0,0 @@
|
||||
ko_fi: captn3m0
|
||||
liberapay: captn3m0
|
||||
github: captn3m0
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -7,19 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## 1.0.2 - 2024-03-07
|
||||
|
||||
### Changed
|
||||
|
||||
- Dependency updates
|
||||
- Fixes for new Outline API
|
||||
|
||||
## 1.0.1 - 2020-04-26
|
||||
|
||||
### Changed
|
||||
|
||||
- Dependency Updates
|
||||
|
||||
## 1.0.0 - 2020-04-26
|
||||
|
||||
### Changed
|
||||
|
16
Dockerfile
16
Dockerfile
@ -1,18 +1,12 @@
|
||||
FROM ruby:3.3-alpine3.19
|
||||
|
||||
RUN apk add --no-cache git openssh-client rsync && \
|
||||
echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \
|
||||
mkdir /root/.ssh
|
||||
FROM ruby:2.7-alpine
|
||||
|
||||
WORKDIR /outliner
|
||||
COPY . /outliner/
|
||||
|
||||
RUN echo "gem: --no-ri --no-rdoc" > ~/.gemrc && \
|
||||
apk add --no-cache alpine-sdk && \
|
||||
gem update --system && \
|
||||
gem install bundler && \
|
||||
RUN gem install bundler && \
|
||||
bundle install && \
|
||||
apk del --no-cache alpine-sdk && \
|
||||
rm ~/.gemrc
|
||||
apk add --no-cache git openssh-client rsync && \
|
||||
echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \
|
||||
mkdir /root/.ssh
|
||||
|
||||
ENTRYPOINT ["/outliner/entrypoint.sh"]
|
||||
|
@ -28,13 +28,6 @@ require 'outliner'
|
||||
client = Outliner.new('https://knowledge.example.com')
|
||||
pp client.auth_info
|
||||
pp client.collections_list(offset: 0, limit: 10)
|
||||
# This works around a 302 redirect bug in httparty
|
||||
begin
|
||||
r = @client.fileOperations__redirect({id: FILE_OPERATION_ID}, format: nil, no_follow: true)
|
||||
rescue HTTParty::RedirectionTooDeep => e
|
||||
# Download this using response = HTTParty.get e.response.header['location'] if needed
|
||||
pp e.response.header['location']
|
||||
end
|
||||
```
|
||||
|
||||
### Import
|
||||
|
@ -20,30 +20,13 @@ local_directory = ARGV[0]
|
||||
CLIENT = Outliner::Client.new ENV['OUTLINE_BASE_URI']
|
||||
|
||||
# Download the complete zip
|
||||
response = CLIENT.collections__export_all(format: "outline-markdown")
|
||||
response = CLIENT.collections_exportAll(download: true)
|
||||
|
||||
raise 'Failed to trigger export_all action' if not response['success']
|
||||
|
||||
file_operation_id = response['data']['fileOperation']['id']
|
||||
fop_info_response = nil
|
||||
i = 0
|
||||
loop do
|
||||
i += 1
|
||||
raise 'Timed out waiting for the file export operation to complete' if i > 20
|
||||
sleep(2*i)
|
||||
fop_info_response = CLIENT.fileOperations__info(id: file_operation_id)
|
||||
raise 'Failed to query export file operation info' if not fop_info_response['ok']
|
||||
break if fop_info_response['data']['state'] == 'complete'
|
||||
end
|
||||
|
||||
begin
|
||||
fop_redirect_response = CLIENT.fileOperations__redirect({id: file_operation_id}, {no_follow: true})
|
||||
rescue HTTParty::RedirectionTooDeep => e
|
||||
response = HTTParty.get e.response.header['location']
|
||||
file = Tempfile.new('download.zip')
|
||||
File.open(file.path, 'w') { |f| f.write(response.body) }
|
||||
`unzip -o "#{file.path}" -d "#{local_directory}"`
|
||||
file.unlink
|
||||
end
|
||||
# Extract it to a tempfle
|
||||
file = Tempfile.new('download.zip')
|
||||
File.open(file.path, 'w') { |f| f.write(response.body) }
|
||||
|
||||
`unzip -o "#{file.path}" -d "#{local_directory}"`
|
||||
|
||||
# Delete tempfile
|
||||
file.unlink
|
||||
|
@ -28,7 +28,7 @@ def create_documents_recursively(directory, collection_id, parent_document_id =
|
||||
}
|
||||
|
||||
params[:parentDocumentId] = parent_document_id if parent_document_id
|
||||
CLIENT.documents__create(params)
|
||||
CLIENT.documents_create(params)
|
||||
puts "[-] #{file}"
|
||||
end
|
||||
|
||||
@ -42,7 +42,7 @@ def create_documents_recursively(directory, collection_id, parent_document_id =
|
||||
publish: true,
|
||||
parentDocumentId: parent_document_id
|
||||
}
|
||||
response = CLIENT.documents__create(params)
|
||||
response = CLIENT.documents_create(params)
|
||||
create_documents_recursively(dir, collection_id, response['data']['id'])
|
||||
end
|
||||
Dir.chdir cwd
|
||||
@ -65,7 +65,7 @@ begin
|
||||
rescue StandardError? => e
|
||||
# If we fail, print an error, and delete the collection
|
||||
puts "[E] Import failed with error: #{e.message}"
|
||||
CLIENT.collections__delete(id: root_collection_id)
|
||||
CLIENT.collections_delete(id: root_collection_id)
|
||||
puts '[E] Deleted collection, please report the issue or retry'
|
||||
exit 1
|
||||
end
|
||||
|
@ -11,28 +11,27 @@ module Outliner
|
||||
end
|
||||
|
||||
def find_or_create_collection(name)
|
||||
collections = self.collections__list(limit: 100)['data']
|
||||
collections = self.collections_list(limit: 100)['data']
|
||||
collections.filter!{|c|c['name'] == name}
|
||||
if collections.size >= 1
|
||||
collections[0]['id']
|
||||
else
|
||||
self.collections__create(name: name, description: 'Imported Collection')['data']['id']
|
||||
self.collections_create(name: name, description: 'Imported Collection')['data']['id']
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(method_name, params = {}, options = {})
|
||||
method_name = "/#{method_name.to_s.sub('__', '.')}"
|
||||
|
||||
def method_missing(method_name, params = {})
|
||||
method_name = '/' + method_name.to_s.sub('_', '.')
|
||||
body = {token: @token}.merge(params).to_json
|
||||
options = {
|
||||
body: params.to_json,
|
||||
body: body,
|
||||
headers: {
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
'User-Agent' => "Outliner/#{Outliner::VERSION}",
|
||||
'Authorization' => "Bearer #{@token}"
|
||||
'Accept'=>'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': "Outliner/#{Outliner::VERSION}"
|
||||
},
|
||||
format: :json,
|
||||
}.merge!(options)
|
||||
format: :json
|
||||
}
|
||||
|
||||
self.class.post(method_name, options)
|
||||
end
|
||||
|
@ -1,3 +1,3 @@
|
||||
module Outliner
|
||||
VERSION = "1.0.2"
|
||||
VERSION = "1.0.0"
|
||||
end
|
||||
|
@ -23,11 +23,10 @@ Gem::Specification.new do |spec|
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_dependency "httparty", "~> 0.21"
|
||||
spec.add_dependency "json", "~> 2.7"
|
||||
spec.add_dependency "httparty", "~> 0.17"
|
||||
|
||||
spec.add_development_dependency "bundler", "~> 2.1"
|
||||
spec.add_dependency "rake", ">= 13.1"
|
||||
spec.add_development_dependency "webmock", "~> 3.23"
|
||||
spec.add_development_dependency "minitest", "~> 5.22"
|
||||
spec.add_development_dependency "bundler", "~> 2.0"
|
||||
spec.add_development_dependency "rake", "~> 10.0"
|
||||
spec.add_development_dependency "webmock", "~> 3.6.0"
|
||||
spec.add_development_dependency "minitest", "~> 5.8.4"
|
||||
end
|
||||
|
31
test/fixtures/collections.export_all.200.json
vendored
31
test/fixtures/collections.export_all.200.json
vendored
@ -1,31 +0,0 @@
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"fileOperation": {
|
||||
"id": "08d5db26-bf43-4ec9-ac62-8769fd828e94",
|
||||
"type": "export",
|
||||
"format": "outline-markdown",
|
||||
"name": "Acme-export.zip",
|
||||
"state": "creating",
|
||||
"error": null,
|
||||
"size": "0",
|
||||
"collectionId": null,
|
||||
"user": {
|
||||
"id": "817fb131-4a9b-4981-9002-38c2503adc3e",
|
||||
"name": "Acme Admin",
|
||||
"avatarUrl": "https://fake-avatar-url.com",
|
||||
"color": "#2BC2FF",
|
||||
"isAdmin": true,
|
||||
"isSuspended": false,
|
||||
"isViewer": false,
|
||||
"createdAt": "2024-03-07T04:03:45.204Z",
|
||||
"updatedAt": "2024-03-07T06:51:26.023Z",
|
||||
"lastActiveAt": "2024-03-07T06:51:26.023Z"
|
||||
},
|
||||
"createdAt": "2024-03-07T06:51:26.031Z",
|
||||
"updatedAt": "2024-03-07T06:51:26.031Z"
|
||||
}
|
||||
},
|
||||
"status": 200,
|
||||
"ok": true
|
||||
}
|
@ -1 +0,0 @@
|
||||
Redirecting to https://fake.s3-accelerate.amazonaws.com/uploads/3e11b7f9-f1c0-44d0-a21b-4d6e0561e9c9/a5b6985a-cff6-4d03-be60-20c517bee63e/Acme-export.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXXXXXXXXXXXXXXXX%2F20240307%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20240307T055812Z&X-Amz-Expires=60&X-Amz-Signature=ff759b27ddfd5c7401c1715411a8ceba886f9f462c9b52fc0c4a5906e99ecd22&X-Amz-SignedHeaders=host&response-content-disposition=attachment.
|
@ -6,7 +6,6 @@ require 'json'
|
||||
class ClientTest < Minitest::Test
|
||||
TOKEN = "c4302eFAKE_TOKEN9b6e27bccb7"
|
||||
BASE_URI='https://kb.example.com'
|
||||
FILE_OPERATION_ID = "08d5db26-bf43-4ec9-ac62-8769fd828e94"
|
||||
def setup
|
||||
ENV['OUTLINE_TOKEN'] = TOKEN
|
||||
@client = Outliner::Client.new BASE_URI
|
||||
@ -17,36 +16,9 @@ class ClientTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_auth_info_api
|
||||
mock('auth.info')
|
||||
r = @client.auth__info
|
||||
assert_equal "https://kb.example.com", r['data']['team']['url']
|
||||
end
|
||||
|
||||
def test_export
|
||||
mock('collections.export_all')
|
||||
r = @client.collections__export_all
|
||||
assert_equal FILE_OPERATION_ID, r['data']['fileOperation']['id']
|
||||
assert_equal 200, r['status']
|
||||
assert_equal true, r['ok']
|
||||
end
|
||||
|
||||
def test_retrieve_file_operation
|
||||
mock("fileOperations.redirect", {
|
||||
id: FILE_OPERATION_ID
|
||||
}, {
|
||||
"X-Download-Options" => "noopen",
|
||||
"X-Content-Type-Options" => "nosniff",
|
||||
"Content-Type" => "text/plain; charset=utf-8",
|
||||
"Content-Length" => "459",
|
||||
"Location" => "https://s3.example.com/#{FILE_OPERATION_ID}"
|
||||
}, 302)
|
||||
begin
|
||||
r = @client.fileOperations__redirect({id: FILE_OPERATION_ID}, format: nil, no_follow: true)
|
||||
rescue HTTParty::RedirectionTooDeep => e
|
||||
assert_equal "302", e.response.code
|
||||
assert_equal "https://s3.example.com/#{FILE_OPERATION_ID}", e.response.header['location']
|
||||
end
|
||||
|
||||
mock('auth.info', 'auth.info.200')
|
||||
auth_info = @client.auth_info
|
||||
assert_equal "https://kb.example.com", auth_info['data']['team']['url']
|
||||
end
|
||||
|
||||
private
|
||||
@ -55,16 +27,15 @@ class ClientTest < Minitest::Test
|
||||
File.read "test/fixtures/#{file}.json"
|
||||
end
|
||||
|
||||
def mock(method_name, params = {}, response_headers = {}, status = 200)
|
||||
def mock(method_name, fixture_file, params = {})
|
||||
stub_request(:post, BASE_URI + "/api/" + method_name)
|
||||
.with(
|
||||
body: params.to_json,
|
||||
body: params.merge({token: TOKEN}).to_json,
|
||||
headers: {
|
||||
'Accept'=>'application/json',
|
||||
'User-Agent'=>"Outliner/#{Outliner::VERSION}",
|
||||
'Content-Type'=> 'application/json',
|
||||
"Authorization"=> "Bearer #{TOKEN}"
|
||||
'Content-Type'=> 'application/json'
|
||||
}
|
||||
).to_return(body: read_fixture(method_name + ".#{status}"), headers: response_headers, status: 302)
|
||||
).to_return(body: read_fixture(fixture_file))
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user