~singpolyma/dhall-ruby

c659fe826f54319d93da32d4b11ae6c672df2a65 — Stephen Paul Weber 2 years ago c55ca97
If trying to import from IPFS, fallback to gateway
5 files changed, 108 insertions(+), 4 deletions(-)

M .rubocop.yml
M Gemfile
M lib/dhall/ast.rb
M lib/dhall/resolve.rb
M test/test_resolve.rb
M .rubocop.yml => .rubocop.yml +3 -0
@@ 67,6 67,9 @@ Style/StringLiteralsInInterpolation:
Style/SymbolArray:
  EnforcedStyle: brackets

Style/WordArray:
  EnforcedStyle: brackets

Naming/UncommunicativeBlockParamName:
  Enabled: false


M Gemfile => Gemfile +1 -0
@@ 5,3 5,4 @@ source "https://rubygems.org"
gem "cbor"
gem "promise.rb"
gem "value_semantics"
gem "webmock"

M lib/dhall/ast.rb => lib/dhall/ast.rb +5 -1
@@ 572,7 572,7 @@ module Dhall
				(uri.scheme == "https" ? Https : Http).new(
					nil,
					"#{uri.host}:#{uri.port}",
					uri.path.split(/\//)[1..-1],
					*uri.path.split(/\//)[1..-1],
					uri.query,
					nil
				)


@@ 636,6 636,10 @@ module Dhall
			def pathname
				Pathname.new("/").join(*path)
			end

			def to_uri(scheme, authority)
				scheme.new(nil, authority, *path, nil, nil)
			end
		end

		class RelativePath < Path

M lib/dhall/resolve.rb => lib/dhall/resolve.rb +69 -3
@@ 21,7 21,9 @@ module Dhall
		ReadHttpSources = lambda do |sources|
			sources.map do |source|
				Promise.resolve(nil).then do
					Net::HTTP.get(source.uri)
					r = Net::HTTP.get_response(source.uri)
					raise ImportFailedException, source if r.code != "200"
					r.body
				end
			end
		end


@@ 32,6 34,50 @@ module Dhall
			end
		end

		class ReadPathAndIPFSSources
			def initialize(
				path_reader: ReadPathSources,
				http_reader: ReadHttpSources,
				https_reader: http_reader,
				public_gateway: "cloudflare-ipfs.com"
			)
				@path_reader = path_reader
				@http_reader = http_reader
				@https_reader = https_reader
				@public_gateway = public_gateway
			end

			def call(sources)
				@path_reader.call(sources).map.with_index do |promise, idx|
					source = sources[idx]
					if source.is_a?(Import::AbsolutePath) &&
					   ["ipfs", "ipns"].include?(source.path.first)
						gateway_fallback(source, promise)
					else
						promise
					end
				end
			end

			def to_proc
				method(:call).to_proc
			end

			protected

			def gateway_fallback(source, promise)
				promise.catch {
					@http_reader.call([
						source.to_uri(Import::Http, "localhost:8000")
					]).first
				}.catch do
					@https_reader.call([
						source.to_uri(Import::Https, @public_gateway)
					]).first
				end
			end
		end

		class ResolutionSet
			attr_reader :reader



@@ 66,7 112,7 @@ module Dhall
			end
		end

		class Default
		class Standard
			def initialize(
				path_reader: ReadPathSources,
				http_reader: ReadHttpSources,


@@ 111,7 157,27 @@ module Dhall
			end
		end

		class LocalOnly < Default
		class Default < Standard
			def initialize(
				path_reader: ReadPathSources,
				http_reader: ReadHttpSources,
				https_reader: http_reader,
				ipfs_public_gateway: "cloudflare-ipfs.com"
			)
				super(
					path_reader:  ReadPathAndIPFSSources.new(
						path_reader:    path_reader,
						http_reader:    http_reader,
						https_reader:   https_reader,
						public_gateway: ipfs_public_gateway
					),
					http_reader:  http_reader,
					https_reader: https_reader
				)
			end
		end

		class LocalOnly < Standard
			def initialize(path_reader: ReadPathSources)
				super(
					path_reader:  path_reader,

M test/test_resolve.rb => test/test_resolve.rb +30 -0
@@ 1,6 1,7 @@
# frozen_string_literal: true

require "base64"
require "webmock/minitest"
require "minitest/autorun"

require "dhall/resolve"


@@ 172,6 173,35 @@ class TestResolve < Minitest::Test
		assert_equal Dhall::Variable["_"], expr.resolve(@resolver).sync
	end

	def test_ipfs
		stub_request(:get, "http://localhost:8000/ipfs/TESTCID")
			.to_return(status: 200, body: "\x00")

		expr = Dhall::Import.new(
			nil,
			Dhall.method(:from_binary),
			Dhall::Import::AbsolutePath.new("ipfs", "TESTCID")
		)

		assert_equal Dhall::Variable["_"], expr.resolve.sync
	end

	def test_ipfs_public_gateway
		stub_request(:get, "http://localhost:8000/ipfs/TESTCID")
			.to_return(status: 500)

		stub_request(:get, "https://cloudflare-ipfs.com/ipfs/TESTCID")
			.to_return(status: 200, body: "\x00")

		expr = Dhall::Import.new(
			nil,
			Dhall.method(:from_binary),
			Dhall::Import::AbsolutePath.new("ipfs", "TESTCID")
		)

		assert_equal Dhall::Variable["_"], expr.resolve.sync
	end

	# Sanity check that all expressions can pass through the resolver
	Pathname.glob(TESTS + "**/*A.dhallb").each do |path|
		test = path.relative_path_from(TESTS).to_s.sub(/A\.dhallb$/, "")