feat: Add marker fetching logic

This commit is contained in:
Yarmo Mackenbach 2023-03-29 13:04:56 +02:00
parent 46cffbf056
commit 870a544550
No known key found for this signature in database
GPG key ID: 37367F4AF4087AD1
5 changed files with 131 additions and 37 deletions

View file

@ -16,7 +16,7 @@ limitations under the License.
const validator = require('validator')
const validUrl = require('valid-url')
const mergeOptions = require('merge-options')
const proofs = require('./proofs')
const request = require('./request')
const verifications = require('./verifications')
const claimDefinitions = require('./claimDefinitions')
const defaults = require('./defaults')
@ -229,10 +229,44 @@ class Claim {
let verificationResult = null
let proofData = null
let markersData = null
let proofFetchError
// Handle markers
try {
proofData = await proofs.fetch(claimData, opts)
markersData = await request.fetchMarkers(claimData, opts)
} catch (err) {
proofFetchError = err
}
if (markersData) {
let shouldSkipMatch = false
markersData.forEach(marker => {
// Skip marker if another was already proven false
if (shouldSkipMatch) return
// Ignore markers that were rejected
if (marker.status !== 'fulfilled') return
let endpointExists
switch (marker.value.data.test.type) {
case E.MarkerTestType.HTTP_ENDPOINT_MUST_EXIST:
endpointExists = marker.value.result && !marker.value.error
if ((endpointExists && marker.value.data.test.inverse) || (!(endpointExists || marker.value.data.test.inverse))) {
shouldSkipMatch = true
}
break
default:
break
}
})
if (shouldSkipMatch) continue
}
// Handle proof
try {
proofData = await request.fetchProof(claimData, opts)
} catch (err) {
proofFetchError = err
}

View file

@ -146,6 +146,20 @@ const ClaimStatus = {
}
Object.freeze(ClaimStatus)
/**
* How to test a marker
* @readonly
* @enum {string}
*/
const MarkerTestType = {
/** HTTP endpoint must exist */
HTTP_ENDPOINT_MUST_EXIST: 'httpEndpointMustExist'
// TODO Implement JSON_CONTAINS
// /** JSON data must contain a certain string */
// JSON_CONTAINS: 'jsonContains'
}
Object.freeze(MarkerTestType)
exports.ProxyPolicy = ProxyPolicy
exports.Fetcher = Fetcher
exports.EntityEncodingFormat = EntityEncodingFormat
@ -154,3 +168,4 @@ exports.ProofFormat = ProofFormat
exports.ClaimFormat = ClaimFormat
exports.ClaimRelation = ClaimRelation
exports.ClaimStatus = ClaimStatus
exports.MarkerTestType = MarkerTestType

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
const Claim = require('./claim')
const claimDefinitions = require('./claimDefinitions')
const proofs = require('./proofs')
const request = require('./request')
const keys = require('./keys')
const signatures = require('./signatures')
const enums = require('./enums')
@ -26,7 +26,7 @@ const fetcher = require('./fetcher')
exports.Claim = Claim
exports.claimDefinitions = claimDefinitions
exports.proofs = proofs
exports.request = request
exports.keys = keys
exports.signatures = signatures
exports.enums = enums

View file

@ -19,7 +19,7 @@ const utils = require('./utils')
const E = require('./enums')
/**
* @module proofs
* @module request
*/
/**
@ -33,7 +33,7 @@ const E = require('./enums')
* @param {object} opts - Options to enable the request
* @returns {Promise<object|string>}
*/
const fetch = (data, opts) => {
const fetchProof = (data, opts) => {
switch (data.proof.request.fetcher) {
case E.Fetcher.HTTP:
data.proof.request.data.format = data.proof.request.format
@ -44,22 +44,49 @@ const fetch = (data, opts) => {
}
if (jsEnv.isNode) {
return handleNodeRequests(data, opts)
return handleNodeRequests(data.proof, opts, false)
}
return handleBrowserRequests(data, opts)
return handleBrowserRequests(data.proof, opts, false)
}
const handleBrowserRequests = (data, opts) => {
/**
* Delegate the marker requests to the correct fetcher.
* This method uses the current environment (browser/node), certain values from
* the `data` parameter and the proxy policy set in the `opts` parameter to
* choose the right approach to fetch the proof. An error will be thrown if no
* approach is possible.
* @async
* @param {object} data - Data from a claim definition
* @param {object} opts - Options to enable the request
* @returns {Promise<Array<object>>}
*/
const fetchMarkers = async (data, opts) => {
const promises = []
if (!(data.markers && data.markers.length > 0)) throw new Error('No markers found')
data.markers.forEach(marker => {
if (jsEnv.isNode) {
promises.push(handleNodeRequests(marker, opts, true))
} else {
promises.push(handleBrowserRequests(marker, opts, true))
}
})
return Promise.allSettled(promises)
}
const handleBrowserRequests = (data, opts, alwaysResolve) => {
switch (opts.proxy.policy) {
case E.ProxyPolicy.ALWAYS:
return createProxyRequestPromise(data, opts)
return createProxyRequestPromise(data, opts, alwaysResolve)
case E.ProxyPolicy.NEVER:
switch (data.proof.request.access) {
switch (data.request.access) {
case E.ProofAccess.GENERIC:
case E.ProofAccess.GRANTED:
return createDefaultRequestPromise(data, opts)
return createDefaultRequestPromise(data, opts, alwaysResolve)
case E.ProofAccess.NOCORS:
case E.ProofAccess.SERVER:
throw new Error(
@ -70,15 +97,13 @@ const handleBrowserRequests = (data, opts) => {
}
case E.ProxyPolicy.ADAPTIVE:
switch (data.proof.request.access) {
switch (data.request.access) {
case E.ProofAccess.GENERIC:
return createFallbackRequestPromise(data, opts)
case E.ProofAccess.NOCORS:
return createProxyRequestPromise(data, opts)
case E.ProofAccess.GRANTED:
return createFallbackRequestPromise(data, opts)
return createFallbackRequestPromise(data, opts, alwaysResolve)
case E.ProofAccess.NOCORS:
case E.ProofAccess.SERVER:
return createProxyRequestPromise(data, opts)
return createProxyRequestPromise(data, opts, alwaysResolve)
default:
throw new Error('Invalid proof access value')
}
@ -88,47 +113,56 @@ const handleBrowserRequests = (data, opts) => {
}
}
const handleNodeRequests = (data, opts) => {
const handleNodeRequests = (data, opts, alwaysResolve) => {
switch (opts.proxy.policy) {
case E.ProxyPolicy.ALWAYS:
return createProxyRequestPromise(data, opts)
return createProxyRequestPromise(data, opts, alwaysResolve)
case E.ProxyPolicy.NEVER:
return createDefaultRequestPromise(data, opts)
return createDefaultRequestPromise(data, opts, alwaysResolve)
case E.ProxyPolicy.ADAPTIVE:
return createFallbackRequestPromise(data, opts)
return createFallbackRequestPromise(data, opts, alwaysResolve)
default:
throw new Error('Invalid proxy policy')
}
}
const createDefaultRequestPromise = (data, opts) => {
const createDefaultRequestPromise = (data, opts, alwaysResolve) => {
return new Promise((resolve, reject) => {
fetcher[data.proof.request.fetcher]
.fn(data.proof.request.data, opts)
fetcher[data.request.fetcher]
.fn(data.request.data, opts)
.then((res) => {
return resolve({
fetcher: data.proof.request.fetcher,
fetcher: data.request.fetcher,
data: data,
viaProxy: false,
result: res
})
})
.catch((err) => {
return reject(err)
if (alwaysResolve) {
return resolve({
fetcher: 'http',
data: data,
viaProxy: true,
error: err
})
} else {
return reject(err)
}
})
})
}
const createProxyRequestPromise = (data, opts) => {
const createProxyRequestPromise = (data, opts, alwaysResolve) => {
return new Promise((resolve, reject) => {
let proxyUrl
try {
proxyUrl = utils.generateProxyURL(
data.proof.request.fetcher,
data.proof.request.data,
data.request.fetcher,
data.request.data,
opts
)
} catch (err) {
@ -137,8 +171,8 @@ const createProxyRequestPromise = (data, opts) => {
const requestData = {
url: proxyUrl,
format: data.proof.request.format,
fetcherTimeout: fetcher[data.proof.request.fetcher].timeout
format: data.request.format,
fetcherTimeout: fetcher[data.request.fetcher].timeout
}
fetcher.http
.fn(requestData, opts)
@ -151,19 +185,28 @@ const createProxyRequestPromise = (data, opts) => {
})
})
.catch((err) => {
return reject(err)
if (alwaysResolve) {
return resolve({
fetcher: 'http',
data: data,
viaProxy: true,
error: err
})
} else {
return reject(err)
}
})
})
}
const createFallbackRequestPromise = (data, opts) => {
const createFallbackRequestPromise = (data, opts, alwaysResolve) => {
return new Promise((resolve, reject) => {
createDefaultRequestPromise(data, opts)
createDefaultRequestPromise(data, opts, alwaysResolve)
.then((res) => {
return resolve(res)
})
.catch((err1) => {
createProxyRequestPromise(data, opts)
createProxyRequestPromise(data, opts, alwaysResolve)
.then((res) => {
return resolve(res)
})
@ -174,4 +217,5 @@ const createFallbackRequestPromise = (data, opts) => {
})
}
exports.fetch = fetch
exports.fetchProof = fetchProof
exports.fetchMarkers = fetchMarkers

View file

@ -38,6 +38,7 @@ const pattern = {
return _.isString(x) || _.isNull(x)
},
},
markers: _.isArray,
proof: {
uri: (x) => {
return _.isString(x) || _.isNull(x)