feat: access-api forwards store/ and upload/ invocations to upload-api#334
Conversation
…d-upload-invocations-to-upload-api
| // which will allow the access-api ucanto server to accept | ||
| // invocations where aud=web3storageDid | ||
| DID: web3storageDid, | ||
| PRIVATE_KEY: privateKeyFromEnv ?? process.env.PRIVATE_KEY, |
There was a problem hiding this comment.
some of this extra stuff is in here because I wanted to support running the tests with the production private key, which works with a non-error result. But if you don't pass that in, the assertions shift slightly to ensuring that it's an error result of the kind that would happen only if the server signer is wrong
Note how this test was run with WEB3_STORAGE_PRIVATE_KEY:
bengo@bengo ~/protocol.ai/w3protocol/packages/access-api ⚡ DID="$PROD_DID_KEY" WEB3_STORAGE_PRIVATE_KEY="$PROD_PRIVATE_KEY" pnpm test -- -f 'upload-api'
> @web3-storage/access-api@4.2.0 test /Users/bengo/protocol.ai/w3protocol/packages/access-api
> pnpm build && mocha --bail --timeout 10s -n no-warnings -n experimental-vm-modules "-f" "upload-api"
> @web3-storage/access-api@4.2.0 build /Users/bengo/protocol.ai/w3protocol/packages/access-api
> scripts/cli.js build
proxy store/list invocations to upload-api
✔ forwards store/list invocations with aud=did:key (648ms)
✔ forwards invocations with aud=did:web:web3.storage (460ms)
✔ errors when a bad delegation is given as proof (320ms)
Store.all
✔ proxies store/add to upload-api (331ms)
✔ proxies store/remove to upload-api (306ms)
✔ proxies store/list to upload-api (385ms)
Upload.all
✔ proxies upload/add to upload-api (303ms)
✔ proxies upload/remove to upload-api (287ms)
✔ proxies upload/list to upload-api (342ms)
9 passing (3s)
Gozala
left a comment
There was a problem hiding this comment.
Added bunch of Nits, which you can completely ignore or address before or after landing. Only thing I do wish is that we had a test(s) where we spin up local store and upload services and route to those in test so that we have passing cases that don't require actual w3up service and all worked in offline mode.
That said it's probably tricky to do it without moving bunch of code from w3infra here so I think it's good enough for now.
| import { Signer } from '@ucanto/principal/ed25519' | ||
| import * as DID from '@ipld/dag-ucan/did' | ||
| // eslint-disable-next-line no-unused-vars | ||
| import * as dagUcan from '@ipld/dag-ucan' |
There was a problem hiding this comment.
Nit: We re-export all of the types from the dag-ucan in the ucanto interface & it would be better to use those instead, so we don't have a chance of having two diff versions of dag-ucan in the tree.
There was a problem hiding this comment.
Thanks, I updated to that, and will prefer importing from ucanto libs in the future instead of dag-ucan.
import { UCAN } from '@ucanto/interface';
import { DID } from '@ucanto/core';
| */ | ||
| export function service(ctx) { | ||
| return { | ||
| ...UploadApiProxyService.create({ |
There was a problem hiding this comment.
Nit: I think ...uploadProvider(), ...storeProvider() would be more aligned with other stuff already in use. Also I think it would be a good idea to make fetch optional so we only need to pass it when overriding default.
| /** | ||
| * @implements {Pick<Map<dagUcan.DID, iucanto.Connection<any>>, 'get'>} | ||
| */ | ||
| class AudienceConnections { |
There was a problem hiding this comment.
Nit: This whole class seems like incidental complexity, things would be a whole lot simpler with just config object and static function for get method.
P.S.: I know that's not a popular opinion, yet I like to share it in a hope to bring peers to a true side :P
There was a problem hiding this comment.
Is this like what you had in mind? b46dd85
P.S.: I know that's not a popular opinion, yet I like to share it in a hope to bring peers to a true side :P
Eh, I don't disagree necessarily. good suggestion.
There was a problem hiding this comment.
Not exactly, sorry for not been very clear. In linked version you still have audienceConnections which has get function. When I criticize classes what I mean is:
Separate data part which is basically a state from the behavior which are all the methods
That way it does not matter how you constructed state, you can just pass it into a function and it will compute whatever. Separating data and behavior de-tangles two allowing you to store data in e.g. indexDB and then apply function after reading it from indexDB without having to built a class first. Not the case here but complex class heirerchies also often entangle initialization order.
I think there are only two valid reasons to resort to a classes with methods:
- When you have to deal with open-ended variant space. In other sometimes you have many different state options but only one of them will be used in practice. In this scenario dispatching from one function introduces unnecessary coordination and defining interface is better option.
- When your methods are simply a convenience sugar for the static functions. I do it all the time to allow method chaining and remove need for importing lot of dependencies. In this case they don't tangle anything they are nothing but a sugar
I would argue use of classes with methods for any other reason just introduces incidental complexity and gets between you and data nothing else.
There was a problem hiding this comment.
@Gozala ah I understand what you mean now.
Separating data and behavior de-tangles two allowing you to store data in e.g. indexDB and then apply function after reading it from indexDB without having to built a class first.
def have run into that. That helped me understand.
Created this issue to that effect storacha/w3infra#127 |
|
cc @hugomrdias |
…tore/upload service types from the capability defs
…ceMethod with defaults
…yProxy (resolves #339 ]
|
i was hoping for another round of review |
|
Upon for testing this should work as expected /**
* @template {API.Ability} A
* @template {API.URI} R
* @template {API.Caveats} C
* @param {API.CapabilityParser<API.Match<API.ParsedCapability<A, R, API.InferCaveats<C>>>>} capability
* @param {import('../bindings').RouteContext} ctx
*/
function createProxyProvider(capability, ctx) {
const provider = Server.provide(
capability,
async ({ capability, invocation }) => {
/** @type {import('@ucanto/interface').ConnectionView<import('@web3-storage/access/types').Service>} */
const connection = connect({
// This should be the same did:web as access api
id: ctx.signer,
encoder: CAR,
decoder: CBOR,
channel: HTTP.open({
// this is the only new global env var
url: new URL('https://staging.up.web3.storage'),
method: 'POST',
}),
})
const inv = ucanto.invoke({
issuer: ctx.signer,
// This should be the same did:web as access api
audience: ctx.signer,
capability,
proofs: [invocation],
expiration: invocation.expiration,
facts: invocation.facts,
nonce: invocation.nonce,
notBefore: invocation.notBefore,
})
const [result] = await connection.execute(
/** @type {API.ServiceInvocation<{can: A, with: R, nb: C}, import('@web3-storage/access/types').Service>} */ (
inv
)
)
return result
}
)
return provider
}
In the service definition ( /**
* @param {import('../bindings').RouteContext} ctx
* @returns {import('@web3-storage/access/types').Service}
*/
export function service(ctx) {
return {
store: {
add: createProxyProvider(Store.add, ctx),
},
.....
}
}and here https://github.com/web3-storage/w3protocol/blob/b77337692d9e4580031c429c429d4055d6f6ebff/packages/access-client/src/types.ts#L67 we need to add store: {
add: ServiceMethod<StoreAdd, StoreAddResponse, Failure>
} |
🤖 I have created a release *beep* *boop* --- ## [4.3.0](access-api-v4.2.0...access-api-v4.3.0) (2023-01-17) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](#334)) ([b773376](b773376)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [2.2.0](capabilities-v2.1.0...capabilities-v2.2.0) (2023-01-30) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](#334)) ([b773376](b773376)) * **capabilities:** implement access/authorize and ./update caps ([#387](#387)) ([4242ce0](4242ce0)), closes [#385](#385) * embedded key resolution ([#312](#312)) ([4da91d5](4da91d5)) * update @ucanto/* to ~4.2.3 ([#405](#405)) ([50c0c80](50c0c80)) * update access-api ucanto proxy to not need a signer ([#390](#390)) ([71cbeb7](71cbeb7)) ### Bug Fixes * fix client cli service did resolve ([#292](#292)) ([6be9608](6be9608)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [9.3.0](access-v9.2.0...access-v9.3.0) (2023-01-30) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](#334)) ([b773376](b773376)) * access-api handling store/info for space not in db returns failure with name ([#391](#391)) ([9610fcf](9610fcf)) * update @ucanto/* to ~4.2.3 ([#405](#405)) ([50c0c80](50c0c80)) * update access-api ucanto proxy to not need a signer ([#390](#390)) ([71cbeb7](71cbeb7)) ### Bug Fixes * remove unecessary awaits ([#352](#352)) ([64da6e5](64da6e5)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [4.3.0](access-api-v4.2.0...access-api-v4.3.0) (2023-01-17) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](#334)) ([6be7217](6be7217)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [2.2.0](capabilities-v2.1.0...capabilities-v2.2.0) (2023-01-30) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](#334)) ([6be7217](6be7217)) * **capabilities:** implement access/authorize and ./update caps ([#387](#387)) ([ebe1032](ebe1032)), closes [#385](#385) * embedded key resolution ([#312](#312)) ([45f367d](45f367d)) * update @ucanto/* to ~4.2.3 ([#405](#405)) ([ec39443](ec39443)) * update access-api ucanto proxy to not need a signer ([#390](#390)) ([163fb74](163fb74)) ### Bug Fixes * fix client cli service did resolve ([#292](#292)) ([45e7ad4](45e7ad4)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [9.3.0](access-v9.2.0...access-v9.3.0) (2023-01-30) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](#334)) ([6be7217](6be7217)) * access-api handling store/info for space not in db returns failure with name ([#391](#391)) ([665dac9](665dac9)) * update @ucanto/* to ~4.2.3 ([#405](#405)) ([ec39443](ec39443)) * update access-api ucanto proxy to not need a signer ([#390](#390)) ([163fb74](163fb74)) ### Bug Fixes * remove unecessary awaits ([#352](#352)) ([2e8c1a1](2e8c1a1)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [4.3.0](storacha/w3up@access-api-v4.2.0...access-api-v4.3.0) (2023-01-17) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](storacha/w3up#334)) ([b773376](storacha/w3up@b773376)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [2.2.0](storacha/w3up@capabilities-v2.1.0...capabilities-v2.2.0) (2023-01-30) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](storacha/w3up#334)) ([b773376](storacha/w3up@b773376)) * **capabilities:** implement access/authorize and ./update caps ([#387](storacha/w3up#387)) ([4242ce0](storacha/w3up@4242ce0)), closes [#385](storacha/w3up#385) * embedded key resolution ([#312](storacha/w3up#312)) ([4da91d5](storacha/w3up@4da91d5)) * update @ucanto/* to ~4.2.3 ([#405](storacha/w3up#405)) ([50c0c80](storacha/w3up@50c0c80)) * update access-api ucanto proxy to not need a signer ([#390](storacha/w3up#390)) ([71cbeb7](storacha/w3up@71cbeb7)) ### Bug Fixes * fix client cli service did resolve ([#292](storacha/w3up#292)) ([6be9608](storacha/w3up@6be9608)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [9.3.0](storacha/w3up@access-v9.2.0...access-v9.3.0) (2023-01-30) ### Features * access-api forwards store/ and upload/ invocations to upload-api ([#334](storacha/w3up#334)) ([b773376](storacha/w3up@b773376)) * access-api handling store/info for space not in db returns failure with name ([#391](storacha/w3up#391)) ([9610fcf](storacha/w3up@9610fcf)) * update @ucanto/* to ~4.2.3 ([#405](storacha/w3up#405)) ([50c0c80](storacha/w3up@50c0c80)) * update access-api ucanto proxy to not need a signer ([#390](storacha/w3up#390)) ([71cbeb7](storacha/w3up@71cbeb7)) ### Bug Fixes * remove unecessary awaits ([#352](storacha/w3up#352)) ([64da6e5](storacha/w3up@64da6e5)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Motivation: