Skip to content

Commit

Permalink
Add Pinning feature for post message (#106)
Browse files Browse the repository at this point in the history
Solution: Change Store publish to handle hashes pinning and add a new method `Pin` to simplify the process.
  • Loading branch information
Rgascoin committed Dec 7, 2022
1 parent f7b1846 commit a1a7fb7
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 9 deletions.
3 changes: 2 additions & 1 deletion src/messages/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Get } from "./get";
import { Publish } from "./publish";
import { Pin } from "./pin";

export { Get, Publish };
export { Get, Publish, Pin };
28 changes: 28 additions & 0 deletions src/messages/store/pin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as base from "../../accounts/account";
import { ItemType, StoreMessage } from "../message";
import { Publish } from "./publish";
import { DEFAULT_API_V2 } from "../../global";

type StorePinConfiguration = {
channel: string;
account: base.Account;
fileHash: string;
storageEngine?: ItemType;
APIServer?: string;
};

/**
* Publishes a store message, containing a hash to pin an IPFS file.
* You also have to provide default message properties, such as the targeted channel or the account used to sign the message.
*
* @param spc The configuration used to pin the file.
*/
export async function Pin(spc: StorePinConfiguration): Promise<StoreMessage> {
return await Publish({
account: spc.account,
channel: spc.channel,
fileHash: spc.fileHash,
APIServer: spc.APIServer || DEFAULT_API_V2,
storageEngine: ItemType.ipfs,
});
}
28 changes: 20 additions & 8 deletions src/messages/store/publish.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import * as base from "../../accounts/account";
import { MessageType, ItemType, StoreContent, StoreMessage } from "../message";
import { ItemType, MessageType, StoreContent, StoreMessage } from "../message";
import { PushFileToStorageEngine, PutContentToStorageEngine } from "../create/publish";
import { SignAndBroadcast } from "../create/signature";
import { RequireOnlyOne } from "../../utils/requiredOnlyOne";

type StorePublishConfiguration = {
channel: string;
account: base.Account;
fileObject: Buffer | Blob;
fileObject?: Buffer | Blob;
fileHash?: string;
storageEngine: ItemType;
APIServer: string;
};
Expand All @@ -17,12 +19,22 @@ type StorePublishConfiguration = {
*
* @param spc The configuration used to publish a store message.
*/
export async function Publish(spc: StorePublishConfiguration): Promise<StoreMessage> {
const hash = await PushFileToStorageEngine({
APIServer: spc.APIServer,
storageEngine: spc.storageEngine,
file: spc.fileObject,
});
export async function Publish(
spc: RequireOnlyOne<StorePublishConfiguration, "fileObject" | "fileHash">,
): Promise<StoreMessage> {
if (!spc.fileObject && !spc.fileHash) throw new Error("You need to specify a File to upload or a Hash to pin.");
if (spc.fileObject && spc.fileHash) throw new Error("You can't pin a file and upload it at the same time.");
if (spc.fileHash && spc.storageEngine !== ItemType.ipfs) throw new Error("You must choose ipfs to pin file.");

const hash =
spc.fileHash ||
(await PushFileToStorageEngine({
APIServer: spc.APIServer,
storageEngine: spc.storageEngine,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
file: spc.fileObject,
}));

const timestamp = Date.now() / 1000;
const content: StoreContent = {
Expand Down
8 changes: 8 additions & 0 deletions src/utils/requiredOnlyOne.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* This type implementation allows to specify for a given T type two field with only one that have to be field.
* You can find more about this here: https://learn.microsoft.com/en-us/javascript/api/@azure/keyvault-certificates/requireatleastone?view=azure-node-latest
*/
export type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
{
[K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>;
}[Keys];
64 changes: 64 additions & 0 deletions tests/messages/store/publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,68 @@ describe("Store message publish", () => {

expect(got).toBe(expected);
});

it("should pin a file and retrieve it correctly", async () => {
const mnemonic = "twenty enough win warrior then fiction smoke tenant juice lift palace inherit";
const account = ethereum.ImportAccountFromMnemonic(mnemonic);
const helloWorldHash = "QmTp2hEo8eXRp6wg7jXv1BLCMh5a4F3B7buAUZNZUu772j";

const hash = await store.Pin({
channel: "TEST",
account: account,
fileHash: helloWorldHash,
});

const response = await store.Get({
fileHash: hash.content.item_hash,
APIServer: DEFAULT_API_V2,
});

const got = ArraybufferToString(response);
const expected = "hello world!";

expect(got).toBe(expected);
});

it("should fail to pin a file at runtime", async () => {
const mnemonic = "twenty enough win warrior then fiction smoke tenant juice lift palace inherit";
const account = ethereum.ImportAccountFromMnemonic(mnemonic);

const helloWorldHash = "QmTp2hEo8eXRp6wg7jXv1BLCMh5a4F3B7buAUZNZUu772j";
const fileContent = readFileSync("./tests/messages/store/testFile.txt");

await expect(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
store.Publish({
channel: "TEST",
APIServer: DEFAULT_API_V2,
account: account,
storageEngine: ItemType.storage,
fileObject: fileContent,
fileHash: helloWorldHash,
}),
).rejects.toThrow("You can't pin a file and upload it at the same time.");

await expect(
store.Publish({
channel: "TEST",
APIServer: DEFAULT_API_V2,
account: account,
storageEngine: ItemType.storage,
fileHash: helloWorldHash,
}),
).rejects.toThrow("You must choose ipfs to pin file.");

await expect(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
store.Publish({
channel: "TEST",
APIServer: DEFAULT_API_V2,
account: account,
storageEngine: ItemType.storage,
}),
).rejects.toThrow("You need to specify a File to upload or a Hash to pin.");
});
});

0 comments on commit a1a7fb7

Please sign in to comment.