quickbase-js 0.1.0-beta.7 → 0.1.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +46 -130
- package/dist/esm/quickbase.d.ts +1292 -125
- package/dist/esm/quickbase.js +666 -190
- package/dist/esm/quickbase.js.map +1 -1
- package/dist/esm/quickbase.min.js +1 -1
- package/dist/esm/quickbase.min.js.map +1 -1
- package/dist/umd/quickbase.umd.js +674 -189
- package/dist/umd/quickbase.umd.js.map +1 -1
- package/dist/umd/quickbase.umd.min.js +1 -1
- package/dist/umd/quickbase.umd.min.js.map +1 -1
- package/open-api/fix-spec-main.ts +69 -68
- package/open-api/generate-override-report.ts +214 -0
- package/open-api/generate-unified-interface.ts +162 -103
- package/open-api/output/override-report.html +11 -0
- package/open-api/output/override-report.json +14395 -58
- package/open-api/output/quickbase-fixed.json +583 -2
- package/open-api/schema/tags/fields.ts +331 -16
- package/open-api/schema/tags/index.ts +2 -4
- package/open-api/types/spec.ts +2 -1
- package/open-api/utils/generateJsDoc.ts +96 -0
- package/package.json +33 -33
- package/src/RateLimitError.ts +12 -0
- package/src/ThrottleBucket.ts +41 -0
- package/src/authorizationStrategy.ts +246 -0
- package/src/generated-unified/QuickbaseClient.ts +809 -119
- package/src/invokeMethod.ts +242 -0
- package/src/quickbaseClient.ts +117 -248
- package/src/rateLimiter.ts +25 -0
- package/src/tokenCache.ts +10 -26
- package/tests/setup.ts +2 -0
- package/tests/vitest/qb/apps/getApp.test.ts +6 -2
- package/tests/vitest/qb/apps/getAppEvents.test.ts +67 -0
- package/tests/vitest/qb/apps/updateApp.test.ts +58 -0
- package/tests/vitest/qb/auth/mockQuickbaseServer.ts +60 -0
- package/tests/vitest/qb/auth/retryUserToken401.test.ts +73 -0
- package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +65 -0
- package/tests/vitest/qb/fields/createField.test.ts +1 -2
- package/tests/vitest/qb/fields/deleteFields.test.ts +10 -16
- package/tests/vitest/qb/fields/getFieldUsage.test.ts +58 -150
- package/tests/vitest/qb/fields/updateField.test.ts +93 -0
- package/tests/vitest/qb/formulas/runFormula.test.ts +96 -0
- package/tests/vitest/qb/index.ts +1 -0
- package/tests/vitest/qb/tables/createRelationship.test.ts +102 -0
- package/tests/vitest/qb/tables/deleteRelationship.test.ts +143 -0
- package/tests/vitest/qb/tables/updateRelationship.test.ts +250 -0
- package/tests/vitest/unit/ThrottleBucket.test.ts +95 -0
- package/tests/vitest/unit/apps/copyApp.test.ts +133 -40
- package/tests/vitest/unit/apps/getAppEvents.test.ts +193 -0
- package/tests/vitest/unit/apps/updateApp.test.ts +412 -0
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +7 -26
- package/tests/vitest/unit/auth/getTempToken.test.ts +42 -18
- package/tests/vitest/unit/auth/retryTempToken401.test.ts +95 -0
- package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +235 -0
- package/tests/vitest/unit/auth/retryUserToken401.test.ts +69 -0
- package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +325 -0
- package/tests/vitest/unit/fields/createField.test.ts +180 -120
- package/tests/vitest/unit/fields/getFieldUsage.test.ts +47 -14
- package/tests/vitest/unit/fields/updateField.test.ts +285 -0
- package/tests/vitest/unit/formulas/runFormula.test.ts +205 -0
- package/tests/vitest/unit/index.ts +2 -1
- package/tests/vitest/unit/inferHttpMethod.test.ts +131 -0
- package/tests/vitest/unit/rateLimit.test.ts +191 -0
- package/tests/vitest/unit/tables/createRelationship.test.ts +212 -0
- package/tests/vitest/unit/tables/deleteRelationship.test.ts +97 -0
- package/tests/vitest/unit/tables/updateRelationship.test.ts +307 -0
- package/tests/vitest/unit/tokenLifespan.test.ts +204 -0
- package/tools/log-gen.ts +20 -7
- package/tools/log-gen.yaml +3446 -1848
- package/tsconfig.json +5 -2
- package/vitest.config.ts +2 -2
- package/notes.txt +0 -158
- package/tests/vitest/unit/auth/retryOn401.test.ts +0 -116
- package/tests/vitest/unit/auth/two401s.test.ts +0 -86
- package/tools/generate-types.ts +0 -29
- package/tools/openapi-schema-validator.ts +0 -38
- package/tools/spec-analyzer.ts +0 -163
- package/tools/split-spec.ts +0 -148
- package/tools/test-import.ts +0 -3
- package/tools/validate-spec.ts +0 -40
- /package/tests/playwright/qb/auth/{tokenPrefetch.test.ts → tempTokenPrefetch.test.ts} +0 -0
- /package/tests/playwright/qb/auth/{tokenRenewal.test.ts → tempTokenRenewal.test.ts} +0 -0
|
@@ -1673,6 +1673,209 @@
|
|
|
1673
1673
|
};
|
|
1674
1674
|
}
|
|
1675
1675
|
|
|
1676
|
+
/* tslint:disable */
|
|
1677
|
+
/* eslint-disable */
|
|
1678
|
+
/**
|
|
1679
|
+
* Quick Base API
|
|
1680
|
+
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
|
1681
|
+
*
|
|
1682
|
+
* The version of the OpenAPI document: 1.0.0
|
|
1683
|
+
*
|
|
1684
|
+
*
|
|
1685
|
+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
1686
|
+
* https://openapi-generator.tech
|
|
1687
|
+
* Do not edit the class manually.
|
|
1688
|
+
*/
|
|
1689
|
+
/**
|
|
1690
|
+
* @export
|
|
1691
|
+
*/
|
|
1692
|
+
const CreateFieldRequestPropertiesOpenTargetInEnum = {
|
|
1693
|
+
SameWindow: 'sameWindow',
|
|
1694
|
+
NewWindow: 'newWindow',
|
|
1695
|
+
Popup: 'popup'
|
|
1696
|
+
};
|
|
1697
|
+
/**
|
|
1698
|
+
* @export
|
|
1699
|
+
*/
|
|
1700
|
+
const CreateFieldRequestPropertiesSummaryFunctionEnum = {
|
|
1701
|
+
Avg: 'AVG',
|
|
1702
|
+
Sum: 'SUM',
|
|
1703
|
+
Max: 'MAX',
|
|
1704
|
+
Min: 'MIN',
|
|
1705
|
+
StdDev: 'STD-DEV',
|
|
1706
|
+
Count: 'COUNT',
|
|
1707
|
+
CombinedText: 'COMBINED-TEXT',
|
|
1708
|
+
CombinedUser: 'COMBINED-USER',
|
|
1709
|
+
DistinctCount: 'DISTINCT-COUNT'
|
|
1710
|
+
};
|
|
1711
|
+
/**
|
|
1712
|
+
* @export
|
|
1713
|
+
*/
|
|
1714
|
+
const CreateFieldRequestPropertiesCurrencyFormatEnum = {
|
|
1715
|
+
Left: 'left',
|
|
1716
|
+
Right: 'right',
|
|
1717
|
+
Middle: 'middle'
|
|
1718
|
+
};
|
|
1719
|
+
/**
|
|
1720
|
+
* @export
|
|
1721
|
+
*/
|
|
1722
|
+
const CreateFieldRequestPropertiesVersionModeEnum = {
|
|
1723
|
+
Keepallversions: 'keepallversions',
|
|
1724
|
+
Keeplastversions: 'keeplastversions'
|
|
1725
|
+
};
|
|
1726
|
+
/**
|
|
1727
|
+
* Check if a given object implements the CreateFieldRequestProperties interface.
|
|
1728
|
+
*/
|
|
1729
|
+
function instanceOfCreateFieldRequestProperties(value) {
|
|
1730
|
+
return true;
|
|
1731
|
+
}
|
|
1732
|
+
function CreateFieldRequestPropertiesFromJSON(json) {
|
|
1733
|
+
return CreateFieldRequestPropertiesFromJSONTyped(json);
|
|
1734
|
+
}
|
|
1735
|
+
function CreateFieldRequestPropertiesFromJSONTyped(json, ignoreDiscriminator) {
|
|
1736
|
+
if (json == null) {
|
|
1737
|
+
return json;
|
|
1738
|
+
}
|
|
1739
|
+
return {
|
|
1740
|
+
'numLines': json['numLines'] == null ? undefined : json['numLines'],
|
|
1741
|
+
'maxLength': json['maxLength'] == null ? undefined : json['maxLength'],
|
|
1742
|
+
'appendOnly': json['appendOnly'] == null ? undefined : json['appendOnly'],
|
|
1743
|
+
'sortAsGiven': json['sortAsGiven'] == null ? undefined : json['sortAsGiven'],
|
|
1744
|
+
'allowMentions': json['allowMentions'] == null ? undefined : json['allowMentions'],
|
|
1745
|
+
'comments': json['comments'] == null ? undefined : json['comments'],
|
|
1746
|
+
'doesTotal': json['doesTotal'] == null ? undefined : json['doesTotal'],
|
|
1747
|
+
'autoSave': json['autoSave'] == null ? undefined : json['autoSave'],
|
|
1748
|
+
'defaultValueLuid': json['defaultValueLuid'] == null ? undefined : json['defaultValueLuid'],
|
|
1749
|
+
'useI18NFormat': json['useI18NFormat'] == null ? undefined : json['useI18NFormat'],
|
|
1750
|
+
'maxVersions': json['maxVersions'] == null ? undefined : json['maxVersions'],
|
|
1751
|
+
'format': json['format'] == null ? undefined : json['format'],
|
|
1752
|
+
'carryChoices': json['carryChoices'] == null ? undefined : json['carryChoices'],
|
|
1753
|
+
'linkText': json['linkText'] == null ? undefined : json['linkText'],
|
|
1754
|
+
'parentFieldId': json['parentFieldId'] == null ? undefined : json['parentFieldId'],
|
|
1755
|
+
'displayTimezone': json['displayTimezone'] == null ? undefined : json['displayTimezone'],
|
|
1756
|
+
'summaryTargetFieldId': json['summaryTargetFieldId'] == null ? undefined : json['summaryTargetFieldId'],
|
|
1757
|
+
'allowNewChoices': json['allowNewChoices'] == null ? undefined : json['allowNewChoices'],
|
|
1758
|
+
'defaultToday': json['defaultToday'] == null ? undefined : json['defaultToday'],
|
|
1759
|
+
'units': json['units'] == null ? undefined : json['units'],
|
|
1760
|
+
'openTargetIn': json['openTargetIn'] == null ? undefined : json['openTargetIn'],
|
|
1761
|
+
'lookupTargetFieldId': json['lookupTargetFieldId'] == null ? undefined : json['lookupTargetFieldId'],
|
|
1762
|
+
'summaryFunction': json['summaryFunction'] == null ? undefined : json['summaryFunction'],
|
|
1763
|
+
'sourceFieldId': json['sourceFieldId'] == null ? undefined : json['sourceFieldId'],
|
|
1764
|
+
'doesAverage': json['doesAverage'] == null ? undefined : json['doesAverage'],
|
|
1765
|
+
'formula': json['formula'] == null ? undefined : json['formula'],
|
|
1766
|
+
'decimalPlaces': json['decimalPlaces'] == null ? undefined : json['decimalPlaces'],
|
|
1767
|
+
'defaultCountryCode': json['defaultCountryCode'] == null ? undefined : json['defaultCountryCode'],
|
|
1768
|
+
'displayMonth': json['displayMonth'] == null ? undefined : json['displayMonth'],
|
|
1769
|
+
'seeVersions': json['seeVersions'] == null ? undefined : json['seeVersions'],
|
|
1770
|
+
'defaultKind': json['defaultKind'] == null ? undefined : json['defaultKind'],
|
|
1771
|
+
'displayEmail': json['displayEmail'] == null ? undefined : json['displayEmail'],
|
|
1772
|
+
'coverText': json['coverText'] == null ? undefined : json['coverText'],
|
|
1773
|
+
'currencySymbol': json['currencySymbol'] == null ? undefined : json['currencySymbol'],
|
|
1774
|
+
'summaryQuery': json['summaryQuery'] == null ? undefined : json['summaryQuery'],
|
|
1775
|
+
'targetFieldId': json['targetFieldId'] == null ? undefined : json['targetFieldId'],
|
|
1776
|
+
'displayUser': json['displayUser'] == null ? undefined : json['displayUser'],
|
|
1777
|
+
'blankIsZero': json['blankIsZero'] == null ? undefined : json['blankIsZero'],
|
|
1778
|
+
'exact': json['exact'] == null ? undefined : json['exact'],
|
|
1779
|
+
'defaultDomain': json['defaultDomain'] == null ? undefined : json['defaultDomain'],
|
|
1780
|
+
'defaultValue': json['defaultValue'] == null ? undefined : json['defaultValue'],
|
|
1781
|
+
'abbreviate': json['abbreviate'] == null ? undefined : json['abbreviate'],
|
|
1782
|
+
'numberFormat': json['numberFormat'] == null ? undefined : json['numberFormat'],
|
|
1783
|
+
'targetTableName': json['targetTableName'] == null ? undefined : json['targetTableName'],
|
|
1784
|
+
'appearsAs': json['appearsAs'] == null ? undefined : json['appearsAs'],
|
|
1785
|
+
'width': json['width'] == null ? undefined : json['width'],
|
|
1786
|
+
'currencyFormat': json['currencyFormat'] == null ? undefined : json['currencyFormat'],
|
|
1787
|
+
'displayDayOfWeek': json['displayDayOfWeek'] == null ? undefined : json['displayDayOfWeek'],
|
|
1788
|
+
'summaryReferenceFieldId': json['summaryReferenceFieldId'] == null ? undefined : json['summaryReferenceFieldId'],
|
|
1789
|
+
'commaStart': json['commaStart'] == null ? undefined : json['commaStart'],
|
|
1790
|
+
'choices': json['choices'] == null ? undefined : json['choices'],
|
|
1791
|
+
'targetTableId': json['targetTableId'] == null ? undefined : json['targetTableId'],
|
|
1792
|
+
'displayRelative': json['displayRelative'] == null ? undefined : json['displayRelative'],
|
|
1793
|
+
'compositeFields': json['compositeFields'] == null ? undefined : json['compositeFields'],
|
|
1794
|
+
'displayCheckboxAsText': json['displayCheckboxAsText'] == null ? undefined : json['displayCheckboxAsText'],
|
|
1795
|
+
'versionMode': json['versionMode'] == null ? undefined : json['versionMode'],
|
|
1796
|
+
'snapFieldId': json['snapFieldId'] == null ? undefined : json['snapFieldId'],
|
|
1797
|
+
'hours24': json['hours24'] == null ? undefined : json['hours24'],
|
|
1798
|
+
'sortAlpha': json['sortAlpha'] == null ? undefined : json['sortAlpha'],
|
|
1799
|
+
'hasExtension': json['hasExtension'] == null ? undefined : json['hasExtension'],
|
|
1800
|
+
'useNewWindow': json['useNewWindow'] == null ? undefined : json['useNewWindow'],
|
|
1801
|
+
'displayAsLink': json['displayAsLink'] == null ? undefined : json['displayAsLink'],
|
|
1802
|
+
'lookupReferenceFieldId': json['lookupReferenceFieldId'] == null ? undefined : json['lookupReferenceFieldId'],
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
function CreateFieldRequestPropertiesToJSON(json) {
|
|
1806
|
+
return CreateFieldRequestPropertiesToJSONTyped(json, false);
|
|
1807
|
+
}
|
|
1808
|
+
function CreateFieldRequestPropertiesToJSONTyped(value, ignoreDiscriminator = false) {
|
|
1809
|
+
if (value == null) {
|
|
1810
|
+
return value;
|
|
1811
|
+
}
|
|
1812
|
+
return {
|
|
1813
|
+
'numLines': value['numLines'],
|
|
1814
|
+
'maxLength': value['maxLength'],
|
|
1815
|
+
'appendOnly': value['appendOnly'],
|
|
1816
|
+
'sortAsGiven': value['sortAsGiven'],
|
|
1817
|
+
'allowMentions': value['allowMentions'],
|
|
1818
|
+
'comments': value['comments'],
|
|
1819
|
+
'doesTotal': value['doesTotal'],
|
|
1820
|
+
'autoSave': value['autoSave'],
|
|
1821
|
+
'defaultValueLuid': value['defaultValueLuid'],
|
|
1822
|
+
'useI18NFormat': value['useI18NFormat'],
|
|
1823
|
+
'maxVersions': value['maxVersions'],
|
|
1824
|
+
'format': value['format'],
|
|
1825
|
+
'carryChoices': value['carryChoices'],
|
|
1826
|
+
'linkText': value['linkText'],
|
|
1827
|
+
'parentFieldId': value['parentFieldId'],
|
|
1828
|
+
'displayTimezone': value['displayTimezone'],
|
|
1829
|
+
'summaryTargetFieldId': value['summaryTargetFieldId'],
|
|
1830
|
+
'allowNewChoices': value['allowNewChoices'],
|
|
1831
|
+
'defaultToday': value['defaultToday'],
|
|
1832
|
+
'units': value['units'],
|
|
1833
|
+
'openTargetIn': value['openTargetIn'],
|
|
1834
|
+
'lookupTargetFieldId': value['lookupTargetFieldId'],
|
|
1835
|
+
'summaryFunction': value['summaryFunction'],
|
|
1836
|
+
'sourceFieldId': value['sourceFieldId'],
|
|
1837
|
+
'doesAverage': value['doesAverage'],
|
|
1838
|
+
'formula': value['formula'],
|
|
1839
|
+
'decimalPlaces': value['decimalPlaces'],
|
|
1840
|
+
'defaultCountryCode': value['defaultCountryCode'],
|
|
1841
|
+
'displayMonth': value['displayMonth'],
|
|
1842
|
+
'seeVersions': value['seeVersions'],
|
|
1843
|
+
'defaultKind': value['defaultKind'],
|
|
1844
|
+
'displayEmail': value['displayEmail'],
|
|
1845
|
+
'coverText': value['coverText'],
|
|
1846
|
+
'currencySymbol': value['currencySymbol'],
|
|
1847
|
+
'summaryQuery': value['summaryQuery'],
|
|
1848
|
+
'targetFieldId': value['targetFieldId'],
|
|
1849
|
+
'displayUser': value['displayUser'],
|
|
1850
|
+
'blankIsZero': value['blankIsZero'],
|
|
1851
|
+
'exact': value['exact'],
|
|
1852
|
+
'defaultDomain': value['defaultDomain'],
|
|
1853
|
+
'defaultValue': value['defaultValue'],
|
|
1854
|
+
'abbreviate': value['abbreviate'],
|
|
1855
|
+
'numberFormat': value['numberFormat'],
|
|
1856
|
+
'targetTableName': value['targetTableName'],
|
|
1857
|
+
'appearsAs': value['appearsAs'],
|
|
1858
|
+
'width': value['width'],
|
|
1859
|
+
'currencyFormat': value['currencyFormat'],
|
|
1860
|
+
'displayDayOfWeek': value['displayDayOfWeek'],
|
|
1861
|
+
'summaryReferenceFieldId': value['summaryReferenceFieldId'],
|
|
1862
|
+
'commaStart': value['commaStart'],
|
|
1863
|
+
'choices': value['choices'],
|
|
1864
|
+
'targetTableId': value['targetTableId'],
|
|
1865
|
+
'displayRelative': value['displayRelative'],
|
|
1866
|
+
'compositeFields': value['compositeFields'],
|
|
1867
|
+
'displayCheckboxAsText': value['displayCheckboxAsText'],
|
|
1868
|
+
'versionMode': value['versionMode'],
|
|
1869
|
+
'snapFieldId': value['snapFieldId'],
|
|
1870
|
+
'hours24': value['hours24'],
|
|
1871
|
+
'sortAlpha': value['sortAlpha'],
|
|
1872
|
+
'hasExtension': value['hasExtension'],
|
|
1873
|
+
'useNewWindow': value['useNewWindow'],
|
|
1874
|
+
'displayAsLink': value['displayAsLink'],
|
|
1875
|
+
'lookupReferenceFieldId': value['lookupReferenceFieldId'],
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1676
1879
|
/* tslint:disable */
|
|
1677
1880
|
/* eslint-disable */
|
|
1678
1881
|
/**
|
|
@@ -1743,6 +1946,7 @@
|
|
|
1743
1946
|
'findEnabled': json['findEnabled'] == null ? undefined : json['findEnabled'],
|
|
1744
1947
|
'doesDataCopy': json['doesDataCopy'] == null ? undefined : json['doesDataCopy'],
|
|
1745
1948
|
'audited': json['audited'] == null ? undefined : json['audited'],
|
|
1949
|
+
'properties': json['properties'] == null ? undefined : CreateFieldRequestPropertiesFromJSON(json['properties']),
|
|
1746
1950
|
};
|
|
1747
1951
|
}
|
|
1748
1952
|
function CreateFieldRequestToJSON(json) {
|
|
@@ -1766,6 +1970,7 @@
|
|
|
1766
1970
|
'findEnabled': value['findEnabled'],
|
|
1767
1971
|
'doesDataCopy': value['doesDataCopy'],
|
|
1768
1972
|
'audited': value['audited'],
|
|
1973
|
+
'properties': CreateFieldRequestPropertiesToJSON(value['properties']),
|
|
1769
1974
|
};
|
|
1770
1975
|
}
|
|
1771
1976
|
|
|
@@ -6361,8 +6566,6 @@
|
|
|
6361
6566
|
function instanceOfUpdateFieldRequest(value) {
|
|
6362
6567
|
if (!('label' in value) || value['label'] === undefined)
|
|
6363
6568
|
return false;
|
|
6364
|
-
if (!('fieldType' in value) || value['fieldType'] === undefined)
|
|
6365
|
-
return false;
|
|
6366
6569
|
return true;
|
|
6367
6570
|
}
|
|
6368
6571
|
function UpdateFieldRequestFromJSON(json) {
|
|
@@ -6374,7 +6577,7 @@
|
|
|
6374
6577
|
}
|
|
6375
6578
|
return {
|
|
6376
6579
|
'label': json['label'],
|
|
6377
|
-
'fieldType': json['fieldType'],
|
|
6580
|
+
'fieldType': json['fieldType'] == null ? undefined : json['fieldType'],
|
|
6378
6581
|
'fieldHelp': json['fieldHelp'] == null ? undefined : json['fieldHelp'],
|
|
6379
6582
|
'addToForms': json['addToForms'] == null ? undefined : json['addToForms'],
|
|
6380
6583
|
'permissions': json['permissions'] == null ? undefined : json['permissions'],
|
|
@@ -6386,6 +6589,7 @@
|
|
|
6386
6589
|
'findEnabled': json['findEnabled'] == null ? undefined : json['findEnabled'],
|
|
6387
6590
|
'doesDataCopy': json['doesDataCopy'] == null ? undefined : json['doesDataCopy'],
|
|
6388
6591
|
'audited': json['audited'] == null ? undefined : json['audited'],
|
|
6592
|
+
'properties': json['properties'] == null ? undefined : CreateFieldRequestPropertiesFromJSON(json['properties']),
|
|
6389
6593
|
};
|
|
6390
6594
|
}
|
|
6391
6595
|
function UpdateFieldRequestToJSON(json) {
|
|
@@ -6409,6 +6613,7 @@
|
|
|
6409
6613
|
'findEnabled': value['findEnabled'],
|
|
6410
6614
|
'doesDataCopy': value['doesDataCopy'],
|
|
6411
6615
|
'audited': value['audited'],
|
|
6616
|
+
'properties': CreateFieldRequestPropertiesToJSON(value['properties']),
|
|
6412
6617
|
};
|
|
6413
6618
|
}
|
|
6414
6619
|
|
|
@@ -9446,40 +9651,27 @@
|
|
|
9446
9651
|
|
|
9447
9652
|
class TokenCache {
|
|
9448
9653
|
cache;
|
|
9449
|
-
|
|
9450
|
-
constructor(
|
|
9451
|
-
// Default 4:50
|
|
9654
|
+
tempTokenLifespan;
|
|
9655
|
+
constructor(tempTokenLifespan = 4 * 60 * 1000 + 50 * 1000) {
|
|
9452
9656
|
this.cache = new Map();
|
|
9453
|
-
this.
|
|
9657
|
+
this.tempTokenLifespan = tempTokenLifespan;
|
|
9454
9658
|
}
|
|
9455
9659
|
get(dbid) {
|
|
9456
9660
|
const entry = this.cache.get(dbid);
|
|
9457
9661
|
const now = Date.now();
|
|
9458
|
-
if (entry && entry.expiresAt > now)
|
|
9662
|
+
if (entry && entry.expiresAt > now)
|
|
9459
9663
|
return entry.token;
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
// New method to get full entry
|
|
9464
|
-
getEntry(dbid) {
|
|
9465
|
-
const entry = this.cache.get(dbid);
|
|
9466
|
-
const now = Date.now();
|
|
9467
|
-
if (entry && entry.expiresAt > now) {
|
|
9468
|
-
return entry;
|
|
9469
|
-
}
|
|
9470
|
-
return undefined; // Expired or not found
|
|
9664
|
+
if (entry)
|
|
9665
|
+
this.cache.delete(dbid); // Clean up expired temp tokens
|
|
9666
|
+
return undefined;
|
|
9471
9667
|
}
|
|
9472
|
-
set(dbid, token) {
|
|
9668
|
+
set(dbid, token, lifespan) {
|
|
9473
9669
|
const now = Date.now();
|
|
9474
9670
|
this.cache.set(dbid, {
|
|
9475
9671
|
token,
|
|
9476
|
-
expiresAt: now + this.
|
|
9672
|
+
expiresAt: now + (lifespan || this.tempTokenLifespan),
|
|
9477
9673
|
});
|
|
9478
9674
|
}
|
|
9479
|
-
dump() {
|
|
9480
|
-
return Array.from(this.cache.entries());
|
|
9481
|
-
}
|
|
9482
|
-
// New method to clear the cache
|
|
9483
9675
|
clear() {
|
|
9484
9676
|
this.cache.clear();
|
|
9485
9677
|
}
|
|
@@ -9493,20 +9685,405 @@
|
|
|
9493
9685
|
.replace(/^(\w)/, (_, c) => c.toLowerCase());
|
|
9494
9686
|
}
|
|
9495
9687
|
|
|
9688
|
+
// src/authorizationStrategy.ts
|
|
9689
|
+
class TempTokenStrategy {
|
|
9690
|
+
tokenCache;
|
|
9691
|
+
initialTempToken;
|
|
9692
|
+
constructor(tokenCache, initialTempToken) {
|
|
9693
|
+
this.tokenCache = tokenCache;
|
|
9694
|
+
this.initialTempToken = initialTempToken;
|
|
9695
|
+
}
|
|
9696
|
+
async getToken(dbid) {
|
|
9697
|
+
return this.tokenCache.get(dbid) || this.initialTempToken;
|
|
9698
|
+
}
|
|
9699
|
+
applyHeaders(headers, token) {
|
|
9700
|
+
headers["Authorization"] = `QB-TEMP-TOKEN ${token}`;
|
|
9701
|
+
}
|
|
9702
|
+
async handleError(status, params, fetchTempToken, attempt, maxAttempts, debug, methodName) {
|
|
9703
|
+
if (status !== 401 || attempt >= maxAttempts - 1)
|
|
9704
|
+
return null;
|
|
9705
|
+
if (debug)
|
|
9706
|
+
console.log(`Authorization error for ${methodName || "method"} (temp token), refreshing token:`);
|
|
9707
|
+
const dbid = extractDbid(params);
|
|
9708
|
+
if (!dbid) {
|
|
9709
|
+
if (debug)
|
|
9710
|
+
console.log(`No dbid available for ${methodName || "method"}, skipping token refresh`);
|
|
9711
|
+
return null;
|
|
9712
|
+
}
|
|
9713
|
+
if (debug)
|
|
9714
|
+
console.log(`Refreshing temp token for dbid: ${dbid}`);
|
|
9715
|
+
const newToken = await fetchTempToken(dbid);
|
|
9716
|
+
this.tokenCache.set(dbid, newToken);
|
|
9717
|
+
return newToken;
|
|
9718
|
+
}
|
|
9719
|
+
}
|
|
9720
|
+
class UserTokenStrategy {
|
|
9721
|
+
userToken;
|
|
9722
|
+
constructor(userToken) {
|
|
9723
|
+
this.userToken = userToken;
|
|
9724
|
+
}
|
|
9725
|
+
async getToken(_dbid) {
|
|
9726
|
+
return this.userToken;
|
|
9727
|
+
}
|
|
9728
|
+
applyHeaders(headers, token) {
|
|
9729
|
+
headers["Authorization"] = `QB-USER-TOKEN ${token}`;
|
|
9730
|
+
}
|
|
9731
|
+
async handleError(status, _params, _fetchTempToken, attempt, maxAttempts, debug, methodName) {
|
|
9732
|
+
if (status !== 401 || attempt >= maxAttempts - 1)
|
|
9733
|
+
return null;
|
|
9734
|
+
if (debug)
|
|
9735
|
+
console.log(`Retrying ${methodName || "method"} with existing user token: ${this.userToken.substring(0, 10)}...`);
|
|
9736
|
+
return this.userToken;
|
|
9737
|
+
}
|
|
9738
|
+
}
|
|
9739
|
+
class SsoTokenStrategy {
|
|
9740
|
+
samlToken;
|
|
9741
|
+
realm;
|
|
9742
|
+
fetchApi;
|
|
9743
|
+
debug;
|
|
9744
|
+
currentToken;
|
|
9745
|
+
constructor(samlToken, realm, fetchApi, debug = false) {
|
|
9746
|
+
this.samlToken = samlToken;
|
|
9747
|
+
this.realm = realm;
|
|
9748
|
+
this.fetchApi = fetchApi;
|
|
9749
|
+
this.debug = debug;
|
|
9750
|
+
}
|
|
9751
|
+
async getToken(_dbid) {
|
|
9752
|
+
if (!this.currentToken) {
|
|
9753
|
+
this.currentToken = await this.fetchSsoToken({
|
|
9754
|
+
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
9755
|
+
requested_token_type: "urn:quickbase:params:oauth:token-type:temp_token",
|
|
9756
|
+
subject_token: this.samlToken,
|
|
9757
|
+
subject_token_type: "urn:ietf:params:oauth:token-type:saml2",
|
|
9758
|
+
});
|
|
9759
|
+
}
|
|
9760
|
+
return this.currentToken;
|
|
9761
|
+
}
|
|
9762
|
+
applyHeaders(headers, token) {
|
|
9763
|
+
headers["Authorization"] = `QB-TEMP-TOKEN ${token}`;
|
|
9764
|
+
}
|
|
9765
|
+
async handleError(status, _params, _fetchTempToken, attempt, maxAttempts, debug, methodName) {
|
|
9766
|
+
if (status !== 401 || attempt >= maxAttempts - 1)
|
|
9767
|
+
return null;
|
|
9768
|
+
if (debug || this.debug) {
|
|
9769
|
+
console.log(`Authorization error for ${methodName || "method"} (SSO), refreshing token`);
|
|
9770
|
+
}
|
|
9771
|
+
const newToken = await this.refreshSsoToken(debug || this.debug);
|
|
9772
|
+
if (newToken) {
|
|
9773
|
+
this.currentToken = newToken;
|
|
9774
|
+
return newToken;
|
|
9775
|
+
}
|
|
9776
|
+
return null;
|
|
9777
|
+
}
|
|
9778
|
+
async refreshSsoToken(debug = false) {
|
|
9779
|
+
const payload = {
|
|
9780
|
+
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
9781
|
+
requested_token_type: "urn:quickbase:params:oauth:token-type:temp_token",
|
|
9782
|
+
subject_token: this.samlToken,
|
|
9783
|
+
subject_token_type: "urn:ietf:params:oauth:token-type:saml2",
|
|
9784
|
+
};
|
|
9785
|
+
const response = await this.fetchApi(`https://api.quickbase.com/v1/auth/oauth/token`, {
|
|
9786
|
+
method: "POST",
|
|
9787
|
+
headers: {
|
|
9788
|
+
"QB-Realm-Hostname": `${this.realm}.quickbase.com`,
|
|
9789
|
+
"Content-Type": "application/json",
|
|
9790
|
+
},
|
|
9791
|
+
body: JSON.stringify(payload),
|
|
9792
|
+
credentials: "omit",
|
|
9793
|
+
});
|
|
9794
|
+
if (!response.ok) {
|
|
9795
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
9796
|
+
if (debug) {
|
|
9797
|
+
console.log(`[SSO Refresh] Failed: ${response.status}`, errorBody);
|
|
9798
|
+
}
|
|
9799
|
+
throw new Error(`SSO token refresh failed: ${errorBody.message || "Unknown error"}`);
|
|
9800
|
+
}
|
|
9801
|
+
const result = await response.json();
|
|
9802
|
+
const newToken = result.access_token;
|
|
9803
|
+
if (!newToken) {
|
|
9804
|
+
throw new Error("No access token returned from SSO token exchange");
|
|
9805
|
+
}
|
|
9806
|
+
if (debug) {
|
|
9807
|
+
console.log(`[SSO Refresh] New token: ${newToken.substring(0, 10)}...`);
|
|
9808
|
+
}
|
|
9809
|
+
return newToken;
|
|
9810
|
+
}
|
|
9811
|
+
async fetchSsoToken(params) {
|
|
9812
|
+
const response = await this.fetchApi(`https://api.quickbase.com/v1/auth/oauth/token`, {
|
|
9813
|
+
method: "POST",
|
|
9814
|
+
headers: {
|
|
9815
|
+
"QB-Realm-Hostname": `${this.realm}.quickbase.com`,
|
|
9816
|
+
"Content-Type": "application/json",
|
|
9817
|
+
},
|
|
9818
|
+
body: JSON.stringify(params),
|
|
9819
|
+
credentials: "omit",
|
|
9820
|
+
});
|
|
9821
|
+
if (!response.ok) {
|
|
9822
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
9823
|
+
if (this.debug) {
|
|
9824
|
+
console.log(`[fetchSsoToken] Failed: ${response.status}`, errorBody);
|
|
9825
|
+
}
|
|
9826
|
+
throw new Error(`SSO token fetch failed: ${errorBody.message || "Unknown error"}`);
|
|
9827
|
+
}
|
|
9828
|
+
const result = await response.json();
|
|
9829
|
+
const newToken = result.access_token;
|
|
9830
|
+
if (!newToken) {
|
|
9831
|
+
throw new Error("No access token returned from SSO token exchange");
|
|
9832
|
+
}
|
|
9833
|
+
if (this.debug) {
|
|
9834
|
+
console.log(`[fetchSsoToken] Fetched token: ${newToken.substring(0, 10)}...`);
|
|
9835
|
+
}
|
|
9836
|
+
return newToken;
|
|
9837
|
+
}
|
|
9838
|
+
}
|
|
9839
|
+
function extractDbid(params) {
|
|
9840
|
+
return (params.dbid ||
|
|
9841
|
+
params.tableId ||
|
|
9842
|
+
params.appId ||
|
|
9843
|
+
params.body?.from ||
|
|
9844
|
+
params.body?.to);
|
|
9845
|
+
}
|
|
9846
|
+
|
|
9847
|
+
// src/RateLimitError.ts
|
|
9848
|
+
class RateLimitError extends Error {
|
|
9849
|
+
status;
|
|
9850
|
+
retryAfter;
|
|
9851
|
+
constructor(message, status, retryAfter) {
|
|
9852
|
+
super(message);
|
|
9853
|
+
this.name = "RateLimitError";
|
|
9854
|
+
this.status = status;
|
|
9855
|
+
this.retryAfter = retryAfter; // Seconds from Retry-After header, if present
|
|
9856
|
+
}
|
|
9857
|
+
}
|
|
9858
|
+
|
|
9859
|
+
// src/invokeMethod.ts
|
|
9860
|
+
async function invokeMethod(methodName, params, methodMap, baseHeaders, authStrategy, rateLimiter, fetchTempToken, transformDates, debug, convertDates) {
|
|
9861
|
+
console.log("[invokeMethod] Starting for method:", methodName);
|
|
9862
|
+
const methodInfo = methodMap[methodName];
|
|
9863
|
+
if (!methodInfo)
|
|
9864
|
+
throw new Error(`Method ${methodName} not found`);
|
|
9865
|
+
const hasBody = "body" in params && params.body !== undefined;
|
|
9866
|
+
const body = hasBody ? params.body : undefined;
|
|
9867
|
+
const restParams = hasBody
|
|
9868
|
+
? Object.fromEntries(Object.entries(params).filter(([key]) => key !== "body"))
|
|
9869
|
+
: { ...params };
|
|
9870
|
+
const requestParameters = {
|
|
9871
|
+
...restParams,
|
|
9872
|
+
...(hasBody ? { generated: body } : {}),
|
|
9873
|
+
};
|
|
9874
|
+
const requestOptions = {
|
|
9875
|
+
credentials: methodName === "getTempTokenDBID" ? "include" : "omit",
|
|
9876
|
+
method: methodInfo.httpMethod,
|
|
9877
|
+
};
|
|
9878
|
+
let dbid = extractDbid(params);
|
|
9879
|
+
console.log("[invokeMethod] Extracted dbid:", dbid);
|
|
9880
|
+
let token = dbid
|
|
9881
|
+
? await authStrategy.getToken(dbid)
|
|
9882
|
+
: await authStrategy.getToken("");
|
|
9883
|
+
console.log("[invokeMethod] Initial token:", token);
|
|
9884
|
+
if (!token && dbid) {
|
|
9885
|
+
token = await fetchTempToken(dbid);
|
|
9886
|
+
authStrategy.applyHeaders(baseHeaders, token);
|
|
9887
|
+
requestOptions.headers = { ...baseHeaders };
|
|
9888
|
+
console.log("[invokeMethod] Fetched new token:", token);
|
|
9889
|
+
}
|
|
9890
|
+
if (token) {
|
|
9891
|
+
authStrategy.applyHeaders(baseHeaders, token);
|
|
9892
|
+
requestOptions.headers = { ...baseHeaders };
|
|
9893
|
+
console.log("[invokeMethod] Token found, headers set:", requestOptions.headers);
|
|
9894
|
+
if (methodName === "getTempTokenDBID") {
|
|
9895
|
+
console.log("[invokeMethod] Using cached token:", token);
|
|
9896
|
+
return { temporaryAuthorization: token };
|
|
9897
|
+
}
|
|
9898
|
+
}
|
|
9899
|
+
if (debug) {
|
|
9900
|
+
console.log(`[${methodName}] requestParameters:`, requestParameters);
|
|
9901
|
+
console.log(`[${methodName}] requestOptions:`, requestOptions);
|
|
9902
|
+
}
|
|
9903
|
+
async function processResponse(rawResponse) {
|
|
9904
|
+
if (debug)
|
|
9905
|
+
console.log(`[${methodName}] rawResponse:`, rawResponse);
|
|
9906
|
+
if (rawResponse instanceof Response) {
|
|
9907
|
+
const contentType = rawResponse.headers
|
|
9908
|
+
.get("Content-Type")
|
|
9909
|
+
?.toLowerCase();
|
|
9910
|
+
if (debug)
|
|
9911
|
+
console.log(`[${methodName}] contentType:`, contentType);
|
|
9912
|
+
if (contentType?.includes("application/json")) {
|
|
9913
|
+
const jsonResponse = await rawResponse.json();
|
|
9914
|
+
return transformDates(jsonResponse, convertDates);
|
|
9915
|
+
}
|
|
9916
|
+
return rawResponse;
|
|
9917
|
+
}
|
|
9918
|
+
if (rawResponse && typeof rawResponse.value === "function") {
|
|
9919
|
+
const response = await rawResponse.value();
|
|
9920
|
+
if (debug)
|
|
9921
|
+
console.log(`[${methodName}] Resolved JSONApiResponse:`, response);
|
|
9922
|
+
return transformDates(response, convertDates);
|
|
9923
|
+
}
|
|
9924
|
+
return transformDates(rawResponse, convertDates);
|
|
9925
|
+
}
|
|
9926
|
+
async function parseErrorResponse(response) {
|
|
9927
|
+
let message = "Unknown error";
|
|
9928
|
+
let status = response.status || 500;
|
|
9929
|
+
try {
|
|
9930
|
+
const contentType = response.headers?.get("Content-Type")?.toLowerCase() ||
|
|
9931
|
+
"application/json";
|
|
9932
|
+
if (contentType.includes("application/json") &&
|
|
9933
|
+
typeof response.json === "function") {
|
|
9934
|
+
const errorBody = await response.json();
|
|
9935
|
+
if (errorBody &&
|
|
9936
|
+
typeof errorBody === "object" &&
|
|
9937
|
+
"message" in errorBody) {
|
|
9938
|
+
message = errorBody.message;
|
|
9939
|
+
}
|
|
9940
|
+
else {
|
|
9941
|
+
message = "Invalid error response format";
|
|
9942
|
+
}
|
|
9943
|
+
}
|
|
9944
|
+
else if (typeof response.text === "function") {
|
|
9945
|
+
message = (await response.text()) || message;
|
|
9946
|
+
}
|
|
9947
|
+
}
|
|
9948
|
+
catch (e) {
|
|
9949
|
+
if (debug)
|
|
9950
|
+
console.log(`[${methodName}] Error parsing response body:`, e);
|
|
9951
|
+
message = "Failed to parse error response";
|
|
9952
|
+
}
|
|
9953
|
+
console.log("[invokeMethod] Parsed error - status:", status, "message:", message);
|
|
9954
|
+
return { message, status };
|
|
9955
|
+
}
|
|
9956
|
+
let attempt = 0;
|
|
9957
|
+
const maxAttempts = rateLimiter.maxRetries + 1;
|
|
9958
|
+
while (attempt < maxAttempts) {
|
|
9959
|
+
console.log("[invokeMethod] Attempt:", attempt + 1, "of", maxAttempts);
|
|
9960
|
+
try {
|
|
9961
|
+
await rateLimiter.throttle();
|
|
9962
|
+
console.log("[invokeMethod] About to call API for method:", methodName);
|
|
9963
|
+
const response = await methodInfo.method(requestParameters, requestOptions);
|
|
9964
|
+
console.log("[invokeMethod] API call completed for method:", methodName);
|
|
9965
|
+
return await processResponse(response);
|
|
9966
|
+
}
|
|
9967
|
+
catch (error) {
|
|
9968
|
+
let status;
|
|
9969
|
+
let message;
|
|
9970
|
+
let response;
|
|
9971
|
+
console.log("[invokeMethod] Caught error:", error);
|
|
9972
|
+
if (error instanceof ResponseError && error.response) {
|
|
9973
|
+
response = error.response;
|
|
9974
|
+
({ message, status } = await parseErrorResponse(response));
|
|
9975
|
+
}
|
|
9976
|
+
else if (error instanceof Response) {
|
|
9977
|
+
response = error;
|
|
9978
|
+
({ message, status } = await parseErrorResponse(response));
|
|
9979
|
+
}
|
|
9980
|
+
else {
|
|
9981
|
+
if (debug)
|
|
9982
|
+
console.log(`[${methodName}] Unexpected error:`, error);
|
|
9983
|
+
throw error; // Rethrow non-response errors
|
|
9984
|
+
}
|
|
9985
|
+
console.log("[invokeMethod] Handling error with status:", status);
|
|
9986
|
+
if (status === 429) {
|
|
9987
|
+
if (!(error instanceof ResponseError)) {
|
|
9988
|
+
throw new Error("Expected ResponseError for 429 handling"); // Shouldn't happen
|
|
9989
|
+
}
|
|
9990
|
+
const delay = await rateLimiter.handle429(error, attempt + 1);
|
|
9991
|
+
if (debug)
|
|
9992
|
+
console.log(`[${methodName}] 429 delay: ${delay}ms`);
|
|
9993
|
+
if (attempt + 1 === maxAttempts) {
|
|
9994
|
+
throw new RateLimitError(`API Error: ${message} (Status: ${status})`, status, response.headers.get("Retry-After")
|
|
9995
|
+
? parseInt(response.headers.get("Retry-After"), 10)
|
|
9996
|
+
: undefined);
|
|
9997
|
+
}
|
|
9998
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
9999
|
+
attempt++;
|
|
10000
|
+
continue;
|
|
10001
|
+
}
|
|
10002
|
+
const newToken = await authStrategy.handleError(status, params, fetchTempToken, attempt, maxAttempts, debug, methodName);
|
|
10003
|
+
if (newToken) {
|
|
10004
|
+
token = newToken;
|
|
10005
|
+
authStrategy.applyHeaders(baseHeaders, token);
|
|
10006
|
+
requestOptions.headers = { ...baseHeaders };
|
|
10007
|
+
attempt++;
|
|
10008
|
+
if (debug)
|
|
10009
|
+
console.log(`[${methodName}] Retrying with token: ${token.substring(0, 10)}...`);
|
|
10010
|
+
continue;
|
|
10011
|
+
}
|
|
10012
|
+
throw new Error(`API Error: ${message} (Status: ${status})`);
|
|
10013
|
+
}
|
|
10014
|
+
}
|
|
10015
|
+
throw new Error(`API Error: Exhausted retries after ${maxAttempts} attempts`);
|
|
10016
|
+
}
|
|
10017
|
+
|
|
10018
|
+
// src/ThrottleBucket.ts
|
|
10019
|
+
class ThrottleBucket {
|
|
10020
|
+
tokens;
|
|
10021
|
+
maxTokens;
|
|
10022
|
+
refillRate; // Tokens per second
|
|
10023
|
+
lastRefill;
|
|
10024
|
+
pending = Promise.resolve(); // Queue for sequential execution
|
|
10025
|
+
constructor(rate, burst) {
|
|
10026
|
+
this.tokens = burst;
|
|
10027
|
+
this.maxTokens = burst;
|
|
10028
|
+
this.refillRate = rate;
|
|
10029
|
+
this.lastRefill = Date.now();
|
|
10030
|
+
}
|
|
10031
|
+
refill() {
|
|
10032
|
+
const now = Date.now();
|
|
10033
|
+
const elapsed = (now - this.lastRefill) / 1000; // Seconds elapsed
|
|
10034
|
+
const newTokens = elapsed * this.refillRate;
|
|
10035
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);
|
|
10036
|
+
this.lastRefill = now;
|
|
10037
|
+
}
|
|
10038
|
+
async acquire() {
|
|
10039
|
+
// Chain the new acquisition onto the pending queue
|
|
10040
|
+
const previous = this.pending;
|
|
10041
|
+
this.pending = (async () => {
|
|
10042
|
+
await previous; // Wait for prior calls to complete
|
|
10043
|
+
this.refill();
|
|
10044
|
+
if (this.tokens >= 1) {
|
|
10045
|
+
this.tokens -= 1;
|
|
10046
|
+
return;
|
|
10047
|
+
}
|
|
10048
|
+
const waitTime = ((1 - this.tokens) / this.refillRate) * 1000; // ms until next token
|
|
10049
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
10050
|
+
this.refill();
|
|
10051
|
+
this.tokens -= 1;
|
|
10052
|
+
})();
|
|
10053
|
+
await this.pending;
|
|
10054
|
+
}
|
|
10055
|
+
}
|
|
10056
|
+
|
|
10057
|
+
class RateLimiter {
|
|
10058
|
+
throttleBucket;
|
|
10059
|
+
maxRetries;
|
|
10060
|
+
retryDelay;
|
|
10061
|
+
constructor(throttleBucket, maxRetries = 3, retryDelay = 1000) {
|
|
10062
|
+
this.throttleBucket = throttleBucket;
|
|
10063
|
+
this.maxRetries = maxRetries;
|
|
10064
|
+
this.retryDelay = retryDelay;
|
|
10065
|
+
}
|
|
10066
|
+
async throttle() {
|
|
10067
|
+
if (this.throttleBucket) {
|
|
10068
|
+
await this.throttleBucket.acquire();
|
|
10069
|
+
}
|
|
10070
|
+
}
|
|
10071
|
+
async handle429(error, attempt) {
|
|
10072
|
+
// Remove the throw here; let invokeMethod handle exhaustion
|
|
10073
|
+
const retryAfter = error.response.headers.get("Retry-After");
|
|
10074
|
+
return retryAfter
|
|
10075
|
+
? parseInt(retryAfter, 10) * 1000
|
|
10076
|
+
: this.retryDelay * Math.pow(2, attempt - 1);
|
|
10077
|
+
}
|
|
10078
|
+
}
|
|
10079
|
+
|
|
10080
|
+
// src/quickbaseClient.ts
|
|
9496
10081
|
const getParamNames = (fn) => fn
|
|
9497
10082
|
.toString()
|
|
9498
10083
|
.slice(fn.toString().indexOf("(") + 1, fn.toString().indexOf(")"))
|
|
9499
10084
|
.split(",")
|
|
9500
10085
|
.map((p) => p.trim().split("=")[0]?.trim())
|
|
9501
10086
|
.filter((p) => p && !p.match(/^\{/) && p !== "options");
|
|
9502
|
-
const extractDbid = (params, errorMessage) => {
|
|
9503
|
-
const dbid = params.dbid || params.tableId || params.appId;
|
|
9504
|
-
if (!dbid) {
|
|
9505
|
-
throw new Error(errorMessage);
|
|
9506
|
-
}
|
|
9507
|
-
return dbid;
|
|
9508
|
-
};
|
|
9509
|
-
// Utility to convert ISO date strings to Date objects recursively, with optional conversion
|
|
9510
10087
|
function transformDates(obj, convertStringsToDates = true) {
|
|
9511
10088
|
if (obj === null || obj === undefined)
|
|
9512
10089
|
return obj;
|
|
@@ -9528,40 +10105,59 @@
|
|
|
9528
10105
|
}
|
|
9529
10106
|
return obj;
|
|
9530
10107
|
}
|
|
10108
|
+
function inferHttpMethod(methodSource, debug) {
|
|
10109
|
+
const methodMatches = [
|
|
10110
|
+
...methodSource.matchAll(/method:\s*['"]?(\w+)['"]?/gi),
|
|
10111
|
+
];
|
|
10112
|
+
const method = methodMatches.length > 0
|
|
10113
|
+
? methodMatches[methodMatches.length - 1][1].toUpperCase()
|
|
10114
|
+
: "GET";
|
|
10115
|
+
if (debug) {
|
|
10116
|
+
console.log(`[inferHttpMethod] Source:`, methodSource);
|
|
10117
|
+
console.log(`[inferHttpMethod] All matches:`, methodMatches.map((m) => m[0]));
|
|
10118
|
+
console.log(`[inferHttpMethod] Extracted method:`, method);
|
|
10119
|
+
}
|
|
10120
|
+
return method;
|
|
10121
|
+
}
|
|
9531
10122
|
function quickbase(config) {
|
|
9532
|
-
const { realm, userToken, tempToken
|
|
9533
|
-
|
|
9534
|
-
const
|
|
9535
|
-
|
|
10123
|
+
const { realm, userToken, tempToken, useTempTokens, useSso, samlToken, fetchApi, debug, convertDates = true, tempTokenLifespan = 290000, throttle = { rate: 10, burst: 10 }, maxRetries = 3, retryDelay = 1000, tokenCache: providedTokenCache, baseUrl = "https://api.quickbase.com/v1", } = config;
|
|
10124
|
+
const tokenCache = providedTokenCache || new TokenCache(tempTokenLifespan);
|
|
10125
|
+
const throttleBucket = throttle
|
|
10126
|
+
? new ThrottleBucket(throttle.rate, throttle.burst)
|
|
10127
|
+
: null;
|
|
10128
|
+
const rateLimiter = new RateLimiter(throttleBucket, maxRetries, retryDelay);
|
|
10129
|
+
const defaultFetch = typeof globalThis.window !== "undefined"
|
|
10130
|
+
? globalThis.window.fetch.bind(globalThis.window)
|
|
10131
|
+
: undefined;
|
|
10132
|
+
const effectiveFetch = fetchApi || defaultFetch;
|
|
10133
|
+
if (!effectiveFetch) {
|
|
10134
|
+
throw new Error("No fetch implementation available");
|
|
10135
|
+
}
|
|
10136
|
+
const authStrategy = useSso
|
|
10137
|
+
? new SsoTokenStrategy(samlToken || "", realm, effectiveFetch, debug)
|
|
10138
|
+
: useTempTokens
|
|
10139
|
+
? new TempTokenStrategy(tokenCache, tempToken)
|
|
10140
|
+
: new UserTokenStrategy(userToken || "");
|
|
9536
10141
|
const baseHeaders = {
|
|
9537
10142
|
"QB-Realm-Hostname": `${realm}.quickbase.com`,
|
|
9538
10143
|
"Content-Type": "application/json",
|
|
9539
10144
|
};
|
|
9540
|
-
if (initialTempToken) {
|
|
9541
|
-
baseHeaders["Authorization"] = `QB-TEMP-TOKEN ${initialTempToken}`;
|
|
9542
|
-
}
|
|
9543
|
-
else if (userToken && !useTempTokens) {
|
|
9544
|
-
baseHeaders["Authorization"] = `QB-USER-TOKEN ${userToken}`;
|
|
9545
|
-
}
|
|
9546
|
-
const defaultFetch = typeof globalThis.window !== "undefined"
|
|
9547
|
-
? globalThis.window.fetch.bind(globalThis.window)
|
|
9548
|
-
: undefined;
|
|
9549
10145
|
const configuration = new Configuration({
|
|
9550
10146
|
basePath: baseUrl,
|
|
9551
10147
|
headers: { ...baseHeaders },
|
|
9552
|
-
fetchApi:
|
|
10148
|
+
fetchApi: effectiveFetch,
|
|
9553
10149
|
credentials: "omit",
|
|
9554
10150
|
});
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
10151
|
+
// Removed redundant check:
|
|
10152
|
+
// if (!configuration.fetchApi && typeof globalThis.window === "undefined") {
|
|
10153
|
+
// throw new Error("fetchApi must be provided in non-browser environments (e.g., Node.js)");
|
|
10154
|
+
// }
|
|
9558
10155
|
const apiInstances = Object.fromEntries(Object.entries(apis)
|
|
9559
10156
|
.filter(([name]) => name.endsWith("Api"))
|
|
9560
10157
|
.map(([name, ApiClass]) => [
|
|
9561
10158
|
name.replace("Api", "").toLowerCase(),
|
|
9562
10159
|
new ApiClass(configuration),
|
|
9563
10160
|
]));
|
|
9564
|
-
const methodMap = buildMethodMap();
|
|
9565
10161
|
function buildMethodMap() {
|
|
9566
10162
|
const methodMap = {};
|
|
9567
10163
|
const isValidMethod = (name) => !name.startsWith("_") &&
|
|
@@ -9573,25 +10169,28 @@
|
|
|
9573
10169
|
typeof api[name] === "function")
|
|
9574
10170
|
.forEach((rawMethodName) => {
|
|
9575
10171
|
const simplifiedName = simplifyName(rawMethodName);
|
|
9576
|
-
const
|
|
10172
|
+
const rawMethodKey = `${rawMethodName}Raw`;
|
|
10173
|
+
const method = api[rawMethodKey] || api[rawMethodName];
|
|
9577
10174
|
const boundMethod = method.bind(api);
|
|
9578
10175
|
if (typeof boundMethod === "function" && boundMethod.length <= 2) {
|
|
10176
|
+
const methodSource = method.toString();
|
|
9579
10177
|
methodMap[simplifiedName] = {
|
|
9580
10178
|
api,
|
|
9581
10179
|
method: boundMethod,
|
|
9582
10180
|
paramMap: getParamNames(method),
|
|
10181
|
+
httpMethod: inferHttpMethod(methodSource, debug),
|
|
9583
10182
|
};
|
|
9584
10183
|
}
|
|
9585
10184
|
});
|
|
9586
10185
|
}
|
|
10186
|
+
if (debug) {
|
|
10187
|
+
console.log("[buildMethodMap] Methods:", Object.keys(methodMap));
|
|
10188
|
+
}
|
|
9587
10189
|
return methodMap;
|
|
9588
10190
|
}
|
|
10191
|
+
const methodMap = buildMethodMap();
|
|
9589
10192
|
const fetchTempToken = async (dbid) => {
|
|
9590
|
-
const
|
|
9591
|
-
if (!effectiveFetch) {
|
|
9592
|
-
throw new Error("No fetch implementation available for fetching temp token");
|
|
9593
|
-
}
|
|
9594
|
-
const response = await effectiveFetch(`https://api.quickbase.com/v1/auth/temporary/${dbid}`, {
|
|
10193
|
+
const response = await effectiveFetch(`${baseUrl}/auth/temporary/${dbid}`, {
|
|
9595
10194
|
method: "GET",
|
|
9596
10195
|
headers: { ...baseHeaders },
|
|
9597
10196
|
credentials: "include",
|
|
@@ -9601,155 +10200,32 @@
|
|
|
9601
10200
|
throw new Error(`API Error: ${errorBody.message || "Unknown error"} (Status: ${response.status})`);
|
|
9602
10201
|
}
|
|
9603
10202
|
const tokenResult = await response.json();
|
|
10203
|
+
console.log(`[fetchTempToken] Response:`, tokenResult);
|
|
9604
10204
|
const token = tokenResult.temporaryAuthorization;
|
|
9605
10205
|
if (!token) {
|
|
9606
10206
|
throw new Error("No temporary token returned from API");
|
|
9607
10207
|
}
|
|
9608
|
-
tokenCache.set(dbid, token);
|
|
10208
|
+
tokenCache.set(dbid, token, tempTokenLifespan);
|
|
9609
10209
|
if (debug) {
|
|
9610
10210
|
console.log(`Fetched and cached new token for dbid: ${dbid}`, token);
|
|
9611
10211
|
}
|
|
9612
10212
|
return token;
|
|
9613
10213
|
};
|
|
9614
|
-
|
|
9615
|
-
const methodInfo = methodMap[methodName];
|
|
9616
|
-
if (!methodInfo) {
|
|
9617
|
-
throw new Error(`Method ${methodName} not found`);
|
|
9618
|
-
}
|
|
9619
|
-
// Safely handle body extraction
|
|
9620
|
-
const hasBody = "body" in params && params.body !== undefined;
|
|
9621
|
-
const body = hasBody ? params.body : undefined;
|
|
9622
|
-
const restParams = hasBody
|
|
9623
|
-
? Object.fromEntries(Object.entries(params).filter(([key]) => key !== "body"))
|
|
9624
|
-
: { ...params };
|
|
9625
|
-
// Construct requestParameters with 'generated' for body
|
|
9626
|
-
const requestParameters = {
|
|
9627
|
-
...restParams,
|
|
9628
|
-
...(body ? { generated: { ...body } } : {}),
|
|
9629
|
-
};
|
|
9630
|
-
let requestOptions = {
|
|
9631
|
-
credentials: "omit",
|
|
9632
|
-
};
|
|
9633
|
-
const selectedToken = initialTempToken || (userToken && !useTempTokens ? userToken : undefined);
|
|
9634
|
-
if (methodName === "getTempTokenDBID" && useTempTokens) {
|
|
9635
|
-
const dbid = extractDbid(params, "No dbid provided for getTempTokenDBID");
|
|
9636
|
-
const cachedToken = tokenCache.get(dbid);
|
|
9637
|
-
if (cachedToken) {
|
|
9638
|
-
return { temporaryAuthorization: cachedToken };
|
|
9639
|
-
}
|
|
9640
|
-
}
|
|
9641
|
-
let authorizationToken = selectedToken;
|
|
9642
|
-
if (useTempTokens && !authorizationToken) {
|
|
9643
|
-
const dbid = extractDbid(params, `No dbid found in params for ${methodName} to fetch temp token`);
|
|
9644
|
-
const cachedToken = tokenCache.get(dbid);
|
|
9645
|
-
authorizationToken = cachedToken || (await fetchTempToken(dbid));
|
|
9646
|
-
if (methodName === "getTempTokenDBID") {
|
|
9647
|
-
return { temporaryAuthorization: authorizationToken };
|
|
9648
|
-
}
|
|
9649
|
-
requestOptions.headers = {
|
|
9650
|
-
...baseHeaders,
|
|
9651
|
-
Authorization: `QB-TEMP-TOKEN ${authorizationToken}`,
|
|
9652
|
-
};
|
|
9653
|
-
}
|
|
9654
|
-
else if (authorizationToken) {
|
|
9655
|
-
requestOptions.headers = {
|
|
9656
|
-
...baseHeaders,
|
|
9657
|
-
Authorization: `QB-USER-TOKEN ${authorizationToken}`,
|
|
9658
|
-
};
|
|
9659
|
-
}
|
|
9660
|
-
if (debug) {
|
|
9661
|
-
console.log(`[${methodName}] requestParameters:`, requestParameters);
|
|
9662
|
-
console.log(`[${methodName}] requestOptions:`, requestOptions);
|
|
9663
|
-
}
|
|
9664
|
-
try {
|
|
9665
|
-
const response = await methodInfo.method(requestParameters, requestOptions);
|
|
9666
|
-
if (debug) {
|
|
9667
|
-
console.log(`[${methodName}] rawResponse:`, response);
|
|
9668
|
-
}
|
|
9669
|
-
if (response instanceof Response) {
|
|
9670
|
-
const contentType = response.headers.get("Content-Type")?.toLowerCase();
|
|
9671
|
-
if (debug) {
|
|
9672
|
-
console.log(`[${methodName}] contentType:`, contentType);
|
|
9673
|
-
}
|
|
9674
|
-
if (contentType?.includes("application/octet-stream")) {
|
|
9675
|
-
return (await response.arrayBuffer());
|
|
9676
|
-
}
|
|
9677
|
-
else if (contentType?.includes("application/x-yaml") ||
|
|
9678
|
-
contentType?.includes("text/yaml")) {
|
|
9679
|
-
return (await response.text());
|
|
9680
|
-
}
|
|
9681
|
-
else if (contentType?.includes("application/json")) {
|
|
9682
|
-
const jsonResponse = await response.json();
|
|
9683
|
-
if (debug) {
|
|
9684
|
-
console.log(`[${methodName}] jsonResponse:`, jsonResponse);
|
|
9685
|
-
}
|
|
9686
|
-
const transformedResponse = transformDates(jsonResponse, convertDates);
|
|
9687
|
-
if (debug) {
|
|
9688
|
-
console.log(`[${methodName}] transformedResponse:`, transformedResponse);
|
|
9689
|
-
}
|
|
9690
|
-
return transformedResponse;
|
|
9691
|
-
}
|
|
9692
|
-
return response;
|
|
9693
|
-
}
|
|
9694
|
-
else {
|
|
9695
|
-
if (debug) {
|
|
9696
|
-
console.log(`[${methodName}] non-Response return, applying transform:`, response);
|
|
9697
|
-
}
|
|
9698
|
-
const transformedResponse = transformDates(response, convertDates);
|
|
9699
|
-
if (debug) {
|
|
9700
|
-
console.log(`[${methodName}] transformedNonResponse:`, transformedResponse);
|
|
9701
|
-
}
|
|
9702
|
-
return transformedResponse;
|
|
9703
|
-
}
|
|
9704
|
-
}
|
|
9705
|
-
catch (error) {
|
|
9706
|
-
if (error instanceof ResponseError &&
|
|
9707
|
-
error.response.status === 401 &&
|
|
9708
|
-
retryCount < 1 &&
|
|
9709
|
-
useTempTokens) {
|
|
9710
|
-
if (debug) {
|
|
9711
|
-
console.log(`Authorization error for ${methodName}, refreshing token:`, error.message);
|
|
9712
|
-
}
|
|
9713
|
-
const dbid = extractDbid(params, `No dbid to refresh token after authorization error`);
|
|
9714
|
-
authorizationToken = await fetchTempToken(dbid);
|
|
9715
|
-
requestOptions.headers = {
|
|
9716
|
-
...baseHeaders,
|
|
9717
|
-
Authorization: `QB-TEMP-TOKEN ${authorizationToken}`,
|
|
9718
|
-
};
|
|
9719
|
-
if (debug) {
|
|
9720
|
-
console.log(`Retrying ${methodName} with new token`);
|
|
9721
|
-
}
|
|
9722
|
-
return invokeMethod(methodName, params, retryCount + 1);
|
|
9723
|
-
}
|
|
9724
|
-
if (error instanceof ResponseError) {
|
|
9725
|
-
let errorMessage = error.message;
|
|
9726
|
-
try {
|
|
9727
|
-
const errorBody = await error.response.json();
|
|
9728
|
-
if (debug) {
|
|
9729
|
-
console.log(`Error response body for ${methodName}:`, errorBody);
|
|
9730
|
-
}
|
|
9731
|
-
errorMessage = errorBody.message || errorMessage;
|
|
9732
|
-
}
|
|
9733
|
-
catch (e) {
|
|
9734
|
-
// Silent fail on parse error
|
|
9735
|
-
}
|
|
9736
|
-
throw new Error(`API Error: ${errorMessage} (Status: ${error.response.status})`);
|
|
9737
|
-
}
|
|
9738
|
-
throw error;
|
|
9739
|
-
}
|
|
9740
|
-
}
|
|
9741
|
-
const proxy = new Proxy({}, {
|
|
10214
|
+
const proxyHandler = {
|
|
9742
10215
|
get: (_, prop) => {
|
|
10216
|
+
console.log("[proxy] Accessing:", prop, "in methodMap:", prop in methodMap);
|
|
9743
10217
|
if (prop in methodMap) {
|
|
9744
10218
|
const methodName = prop;
|
|
9745
|
-
return (params) => invokeMethod(methodName, params);
|
|
10219
|
+
return (params) => invokeMethod(methodName, params, methodMap, baseHeaders, authStrategy, rateLimiter, fetchTempToken, transformDates, debug, convertDates);
|
|
9746
10220
|
}
|
|
10221
|
+
console.log("[proxy] Method not found:", prop);
|
|
9747
10222
|
return undefined;
|
|
9748
10223
|
},
|
|
9749
|
-
}
|
|
10224
|
+
};
|
|
10225
|
+
const proxy = new Proxy({}, proxyHandler);
|
|
9750
10226
|
if (debug) {
|
|
9751
10227
|
console.log("[createClient] Config:", config);
|
|
9752
|
-
console.log("[createClient]
|
|
10228
|
+
console.log("[createClient] Proxy created:", proxy);
|
|
9753
10229
|
}
|
|
9754
10230
|
return proxy;
|
|
9755
10231
|
}
|
|
@@ -9873,6 +10349,14 @@
|
|
|
9873
10349
|
exports.CreateFieldRequestFieldTypeEnum = CreateFieldRequestFieldTypeEnum;
|
|
9874
10350
|
exports.CreateFieldRequestFromJSON = CreateFieldRequestFromJSON;
|
|
9875
10351
|
exports.CreateFieldRequestFromJSONTyped = CreateFieldRequestFromJSONTyped;
|
|
10352
|
+
exports.CreateFieldRequestPropertiesCurrencyFormatEnum = CreateFieldRequestPropertiesCurrencyFormatEnum;
|
|
10353
|
+
exports.CreateFieldRequestPropertiesFromJSON = CreateFieldRequestPropertiesFromJSON;
|
|
10354
|
+
exports.CreateFieldRequestPropertiesFromJSONTyped = CreateFieldRequestPropertiesFromJSONTyped;
|
|
10355
|
+
exports.CreateFieldRequestPropertiesOpenTargetInEnum = CreateFieldRequestPropertiesOpenTargetInEnum;
|
|
10356
|
+
exports.CreateFieldRequestPropertiesSummaryFunctionEnum = CreateFieldRequestPropertiesSummaryFunctionEnum;
|
|
10357
|
+
exports.CreateFieldRequestPropertiesToJSON = CreateFieldRequestPropertiesToJSON;
|
|
10358
|
+
exports.CreateFieldRequestPropertiesToJSONTyped = CreateFieldRequestPropertiesToJSONTyped;
|
|
10359
|
+
exports.CreateFieldRequestPropertiesVersionModeEnum = CreateFieldRequestPropertiesVersionModeEnum;
|
|
9876
10360
|
exports.CreateFieldRequestToJSON = CreateFieldRequestToJSON;
|
|
9877
10361
|
exports.CreateFieldRequestToJSONTyped = CreateFieldRequestToJSONTyped;
|
|
9878
10362
|
exports.CreateRelationship200ResponseFromJSON = CreateRelationship200ResponseFromJSON;
|
|
@@ -10381,6 +10865,7 @@
|
|
|
10381
10865
|
exports.instanceOfCreateAppRequest = instanceOfCreateAppRequest;
|
|
10382
10866
|
exports.instanceOfCreateField200Response = instanceOfCreateField200Response;
|
|
10383
10867
|
exports.instanceOfCreateFieldRequest = instanceOfCreateFieldRequest;
|
|
10868
|
+
exports.instanceOfCreateFieldRequestProperties = instanceOfCreateFieldRequestProperties;
|
|
10384
10869
|
exports.instanceOfCreateRelationship200Response = instanceOfCreateRelationship200Response;
|
|
10385
10870
|
exports.instanceOfCreateRelationshipRequest = instanceOfCreateRelationshipRequest;
|
|
10386
10871
|
exports.instanceOfCreateSolution200Response = instanceOfCreateSolution200Response;
|