Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.2.0 Ready : DEV > MAIN #46

Merged
merged 4 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const dataNft = new DataNft({

// Create a new DataNft object from API
const nonce = 1;
const nft = await DataNft.createFromApi(nonce);
const nft = await DataNft.createFromApi({ nonce });

// Create a new DataNft object from API Response
const response = await fetch('https://devnet-api.multiversx.com/address/nfts');
Expand All @@ -60,7 +60,7 @@ const dataNfts = [];
dataNfts = await DataNft.ownedByAddress(address);

// Retrieves the specific DataNft
const dataNft = DataNft.createFromApi(nonce);
const dataNft = DataNft.createFromApi({ nonce });

// (A) Get a message from the Data Marshal node for your to sign to prove ownership
const message = await dataNft.messageToSign();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@itheum/sdk-mx-data-nft",
"version": "1.1.0",
"version": "1.2.0",
"description": "SDK for Itheum's Data NFT Technology on MultiversX Blockchain",
"main": "out/index.js",
"types": "out/index.d.js",
Expand Down
45 changes: 44 additions & 1 deletion src/datanft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
createNftIdentifier,
numberToPaddedHex,
parseDataNft,
validateSpecificParamsViewData
validateSpecificParamsViewData,
checkStatus
} from './utils';
import minterAbi from './abis/datanftmint.abi.json';
import { NftType, ViewDataReturnType } from './interfaces';
Expand Down Expand Up @@ -267,6 +268,7 @@ export class DataNft {
* @param fwdAllHeaders [optional] Forward all request headers to the Origin Data Stream server.
* @param fwdHeaderKeys [optional] Forward only selected headers to the Origin Data Stream server. Has priority over fwdAllHeaders param. A comma separated lowercase string with less than 5 items. e.g. cookie,authorization
* @param fwdHeaderMapLookup [optional] Used with fwdHeaderKeys to set a front-end client side lookup map of headers the SDK uses to setup the forward. e.g. { cookie : "xyz", authorization : "Bearer zxy" }. Note that these are case-sensitive and need to match fwdHeaderKeys exactly.
* @param nestedIdxToStream [optional] If you are accessing a "nested stream", this is the index of the nested item you want drill into and fetch
*/
async viewData(p: {
signedMessage: string;
Expand All @@ -277,6 +279,7 @@ export class DataNft {
fwdHeaderMapLookup?: {
[key: string]: any;
};
nestedIdxToStream?: number;
}): Promise<ViewDataReturnType> {
DataNft.ensureNetworkConfigSet();
if (!this.dataMarshal) {
Expand All @@ -298,6 +301,7 @@ export class DataNft {
fwdAllHeaders: p.fwdAllHeaders,
fwdHeaderKeys: p.fwdHeaderKeys,
fwdHeaderMapLookup: p.fwdHeaderMapLookup,
nestedIdxToStream: p.nestedIdxToStream,
_mandatoryParamsList: ['signedMessage', 'signableMessage']
});

Expand Down Expand Up @@ -354,6 +358,10 @@ export class DataNft {
url += p.fwdAllHeaders ? '&fwdAllHeaders=1' : '';
}

if (typeof p.nestedIdxToStream !== 'undefined') {
url += `&nestedIdxToStream=${p.nestedIdxToStream}`;
}

if (typeof p.fwdHeaderKeys !== 'undefined') {
url += `&fwdHeaderKeys=${p.fwdHeaderKeys}`;

Expand All @@ -375,6 +383,20 @@ export class DataNft {
const contentType = response.headers.get('content-type');
const data = await response.blob();

// if the marshal returned a error, we should throw it here so that the SDK integrator can handle it
// ... if we don't, the marshal error response is just passed through as a normal data stream response
// ... and the user won't know what went wrong
try {
checkStatus(response);
} catch (e: any) {
// as it's a data marshal error, we get it's payload which is in JSON and send that thrown as text
const errorPayload = await (data as Blob).text();

throw new Error(
`${e.toString()}. Detailed error trace follows : ${errorPayload}`
);
}

return {
data: data,
contentType: contentType || ''
Expand All @@ -396,6 +418,7 @@ export class DataNft {
* @param fwdHeaderKeys [optional] Forward only selected headers to the Origin Data Stream server. Has priority over fwdAllHeaders param. A comma separated lowercase string with less than 5 items. e.g. cookie,authorization
* @param fwdAllHeaders [optional] Forward all request headers to the Origin Data Stream server.
* @param stream [optional] Instead of auto-downloading if possible, request if data should always be streamed or not.i.e true=stream, false/undefined=default behavior
* @param nestedIdxToStream [optional] If you are accessing a "nested stream", this is the index of the nested item you want drill into and fetch
*/
async viewDataViaMVXNativeAuth(p: {
mvxNativeAuthOrigins: string[];
Expand All @@ -406,6 +429,7 @@ export class DataNft {
fwdHeaderKeys?: string;
fwdAllHeaders?: boolean;
stream?: boolean;
nestedIdxToStream?: number;
}): Promise<ViewDataReturnType> {
try {
// S: run any format specific validation
Expand All @@ -416,6 +440,7 @@ export class DataNft {
fwdHeaderMapLookup: p.fwdHeaderMapLookup,
fwdAllHeaders: p.fwdAllHeaders,
stream: p.stream,
nestedIdxToStream: p.nestedIdxToStream,
_fwdHeaderMapLookupMustContainBearerAuthHeader: true,
_mandatoryParamsList: [
'mvxNativeAuthOrigins',
Expand Down Expand Up @@ -466,6 +491,10 @@ export class DataNft {
url += p.fwdAllHeaders ? '&fwdAllHeaders=1' : '';
}

if (typeof p.nestedIdxToStream !== 'undefined') {
url += `&nestedIdxToStream=${p.nestedIdxToStream}`;
}

// if fwdHeaderMapLookup exists, send these headers and values to the data marshal for forwarding
if (
typeof p.fwdHeaderMapLookup !== 'undefined' &&
Expand Down Expand Up @@ -495,6 +524,20 @@ export class DataNft {
const contentType = response.headers.get('content-type');
const data = await response.blob();

// if the marshal returned a error, we should throw it here so that the SDK integrator can handle it
// ... if we don't, the marshal error response is just passed through as a normal data stream response
// ... and the user won't know what went wrong
try {
checkStatus(response);
} catch (e: any) {
// as it's a data marshal error, we get it's payload which is in JSON and send that thrown as text
const errorPayload = await (data as Blob).text();

throw new Error(
`${e.toString()}. Detailed error trace follows : ${errorPayload}`
);
}

return {
data: data,
contentType: contentType || ''
Expand Down
38 changes: 37 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function validateSpecificParamsViewData(params: {
mvxNativeAuthMaxExpirySeconds?: number | undefined;
mvxNativeAuthOrigins?: string[] | undefined;
fwdHeaderMapLookup?: any;
nestedIdxToStream?: number | undefined;
_fwdHeaderMapLookupMustContainBearerAuthHeader?: boolean | undefined;
_mandatoryParamsList: string[]; // a pure JS fallback way to validate mandatory params, as typescript rules for mandatory can be bypassed by client app
}): {
Expand Down Expand Up @@ -301,6 +302,32 @@ export function validateSpecificParamsViewData(params: {
}
}

// nestedIdxToStream test
let nestedIdxToStreamValid = true;

if (
params.nestedIdxToStream !== undefined ||
params._mandatoryParamsList.includes('nestedIdxToStream')
) {
nestedIdxToStreamValid = false;

const nestedIdxToStreamToInt =
params.nestedIdxToStream !== undefined
? parseInt(params.nestedIdxToStream.toString(), 10)
: null;

if (
nestedIdxToStreamToInt !== null &&
!isNaN(nestedIdxToStreamToInt) &&
nestedIdxToStreamToInt >= 0
) {
nestedIdxToStreamValid = true;
} else {
validationMessages +=
'[nestedIdxToStream needs to be a number more than 0]';
}
}

if (
!signedMessageValid ||
!signableMessageValid ||
Expand All @@ -309,7 +336,8 @@ export function validateSpecificParamsViewData(params: {
!fwdHeaderKeysIsValid ||
!fwdHeaderMapLookupIsValid ||
!mvxNativeAuthMaxExpirySecondsValid ||
!mvxNativeAuthOriginsIsValid
!mvxNativeAuthOriginsIsValid ||
!nestedIdxToStreamValid
) {
allPassed = false;
}
Expand Down Expand Up @@ -532,3 +560,11 @@ export async function checkUrlIsUp(url: string, expectedHttpCodes: number[]) {
);
}
}

export function checkStatus(response: Response) {
if (!(response.status >= 200 && response.status <= 299)) {
throw new Error(
`Response returned non-success HTTP code. status = ${response?.status} statusText = ${response?.statusText}`
);
}
}