@morscherlab/mint-sdk 1.1.0-beta.1 → 1.1.0-beta.2

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.
@@ -687,7 +687,79 @@ describe('usePluginClient', () => {
687
687
  expect(requestConfigs).toHaveLength(0)
688
688
  })
689
689
 
690
- it('loads and saves plugin settings through the integrated platform config endpoint', async () => {
690
+ it.each([
691
+ 'platform',
692
+ undefined,
693
+ ] as const)(
694
+ 'should use the integrated platform config facade when settings_api is %s',
695
+ async (settingsApi) => {
696
+ ;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
697
+ isIntegrated: true,
698
+ theme: 'light',
699
+ platformApiUrl: '/api',
700
+ plugin: {
701
+ id: 'drp',
702
+ name: 'drp',
703
+ version: '1.0.0',
704
+ route_prefix: '/drp',
705
+ api_prefix: '/api/drp',
706
+ ...(settingsApi ? { settings_api: settingsApi } : {}),
707
+ },
708
+ }
709
+ nextGetData = {
710
+ plugin_name: 'drp',
711
+ config: {
712
+ nasBasePath: '/data',
713
+ allowed_experiment_types: ['metabolomics'],
714
+ },
715
+ revision: 'revision-1',
716
+ }
717
+ nextPatchData = {
718
+ plugin_name: 'drp',
719
+ config: {
720
+ nasBasePath: '/mnt',
721
+ allowed_experiment_types: ['metabolomics'],
722
+ },
723
+ revision: 'revision-2',
724
+ }
725
+
726
+ const settings = usePluginSettings<{ nasBasePath?: string }>({ pluginName: 'drp' })
727
+ await settings.load()
728
+
729
+ expect(requestConfigs[0]!.baseURL).toBe('/api')
730
+ expect(requestConfigs[0]!.url).toBe('/plugins/drp/config')
731
+ expect(settings.values.value).toEqual({
732
+ nasBasePath: '/data',
733
+ allowed_experiment_types: ['metabolomics'],
734
+ })
735
+ expect(settings.settings.value).toEqual({
736
+ nasBasePath: '/data',
737
+ allowed_experiment_types: ['metabolomics'],
738
+ })
739
+ expect(settings.isDirty.value).toBe(false)
740
+
741
+ settings.setValues({
742
+ nasBasePath: '/mnt',
743
+ allowed_experiment_types: ['metabolomics'],
744
+ })
745
+ expect(settings.isDirty.value).toBe(true)
746
+ const ok = await settings.save()
747
+
748
+ expect(ok).toBe(true)
749
+ expect(requestConfigs[1]!.baseURL).toBe('/api')
750
+ expect(requestConfigs[1]!.url).toBe('/plugins/drp/config')
751
+ expect(requestMethods[1]).toBe('patch')
752
+ expect(requestConfigs[1]!.headers).toBeUndefined()
753
+ expect(requestBodies[0]).toEqual({ config: { nasBasePath: '/mnt' } })
754
+ expect(settings.values.value).toEqual({
755
+ nasBasePath: '/mnt',
756
+ allowed_experiment_types: ['metabolomics'],
757
+ })
758
+ expect(settings.isDirty.value).toBe(false)
759
+ },
760
+ )
761
+
762
+ it('should use the plugin settings router for an integrated plugin capability', async () => {
691
763
  ;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
692
764
  isIntegrated: true,
693
765
  theme: 'light',
@@ -698,35 +770,271 @@ describe('usePluginClient', () => {
698
770
  version: '1.0.0',
699
771
  route_prefix: '/drp',
700
772
  api_prefix: '/api/drp',
773
+ settings_api: 'plugin',
701
774
  },
702
775
  }
703
- nextGetData = { plugin_name: 'drp', config: { nasBasePath: '/data' } }
704
- nextPatchData = { plugin_name: 'drp', config: { nasBasePath: '/mnt' } }
776
+ nextGetData = {
777
+ settings: { threshold: 1 },
778
+ mode: 'integrated',
779
+ revision: 'revision-1',
780
+ }
781
+ nextPutData = {
782
+ settings: { threshold: 2 },
783
+ mode: 'integrated',
784
+ revision: 'revision-2',
785
+ }
705
786
 
706
- const settings = usePluginSettings<{ nasBasePath?: string }>({ pluginName: 'drp' })
787
+ const settings = usePluginSettings<{ threshold?: number }>({ pluginName: 'drp' })
707
788
  await settings.load()
789
+ const saved = await settings.save({ threshold: 2 })
708
790
 
709
- expect(requestConfigs[0]!.baseURL).toBe('/api')
710
- expect(requestConfigs[0]!.url).toBe('/plugins/drp/config')
711
- expect(settings.values.value).toEqual({ nasBasePath: '/data' })
712
- expect(settings.settings.value).toEqual({ nasBasePath: '/data' })
713
- expect(settings.isDirty.value).toBe(false)
791
+ expect({
792
+ saved,
793
+ methods: requestMethods,
794
+ requests: requestConfigs.map(({ baseURL, url, headers }) => ({
795
+ baseURL,
796
+ url,
797
+ headers,
798
+ })),
799
+ bodies: requestBodies,
800
+ values: settings.values.value,
801
+ }).toEqual({
802
+ saved: true,
803
+ methods: ['get', 'put'],
804
+ requests: [
805
+ {
806
+ baseURL: '/api/drp',
807
+ url: '/settings',
808
+ headers: undefined,
809
+ },
810
+ {
811
+ baseURL: '/api/drp',
812
+ url: '/settings',
813
+ headers: { 'If-Match': '"revision-1"' },
814
+ },
815
+ ],
816
+ bodies: [{ threshold: 2 }],
817
+ values: { threshold: 2 },
818
+ })
819
+ })
714
820
 
715
- settings.setValues({ nasBasePath: '/mnt' })
716
- expect(settings.isDirty.value).toBe(true)
717
- const ok = await settings.save()
821
+ it('should use the platform facade for an explicit different plugin name', async () => {
822
+ ;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
823
+ isIntegrated: true,
824
+ theme: 'light',
825
+ platformApiUrl: '/api',
826
+ plugin: {
827
+ id: 'current-plugin',
828
+ name: 'current-plugin',
829
+ version: '1.0.0',
830
+ route_prefix: '/current-plugin',
831
+ api_prefix: '/api/current-plugin',
832
+ settings_api: 'plugin',
833
+ },
834
+ }
835
+ nextGetData = {
836
+ plugin_name: 'other-plugin',
837
+ config: { threshold: 1 },
838
+ revision: 'revision-1',
839
+ }
718
840
 
719
- expect(ok).toBe(true)
720
- expect(requestConfigs[1]!.baseURL).toBe('/api')
721
- expect(requestConfigs[1]!.url).toBe('/plugins/drp/config')
722
- expect(requestBodies[0]).toEqual({ config: { nasBasePath: '/mnt' } })
723
- expect(settings.values.value).toEqual({ nasBasePath: '/mnt' })
724
- expect(settings.isDirty.value).toBe(false)
841
+ const settings = usePluginSettings<{ threshold?: number }>({
842
+ pluginName: 'other-plugin',
843
+ })
844
+ await settings.load()
845
+
846
+ expect({
847
+ method: requestMethods[0],
848
+ baseURL: requestConfigs[0]!.baseURL,
849
+ url: requestConfigs[0]!.url,
850
+ values: settings.values.value,
851
+ }).toEqual({
852
+ method: 'get',
853
+ baseURL: '/api',
854
+ url: '/plugins/other-plugin/config',
855
+ values: { threshold: 1 },
856
+ })
857
+ })
858
+
859
+ it('should split changed platform-owned settings from a versioned typed save', async () => {
860
+ ;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
861
+ isIntegrated: true,
862
+ theme: 'light',
863
+ platformApiUrl: '/api',
864
+ plugin: {
865
+ id: 'drp',
866
+ name: 'drp',
867
+ version: '1.0.0',
868
+ route_prefix: '/drp',
869
+ api_prefix: '/api/drp',
870
+ },
871
+ }
872
+ nextGetData = {
873
+ plugin_name: 'drp',
874
+ config: {
875
+ threshold: 1,
876
+ allowed_experiment_types: ['metabolomics'],
877
+ },
878
+ revision: 'revision-1',
879
+ }
880
+ nextPatchData = {
881
+ plugin_name: 'drp',
882
+ config: {
883
+ threshold: 2,
884
+ allowed_experiment_types: ['proteomics'],
885
+ },
886
+ revision: 'untrusted-platform-revision',
887
+ }
888
+ const settings = usePluginSettings<{ threshold?: number }>({ pluginName: 'drp' })
889
+ await settings.load()
890
+ settings.setValues({
891
+ threshold: 2,
892
+ allowed_experiment_types: ['proteomics'],
893
+ })
894
+
895
+ const saved = await settings.save()
896
+ nextPatchData = {
897
+ plugin_name: 'drp',
898
+ config: {
899
+ threshold: 3,
900
+ allowed_experiment_types: ['proteomics'],
901
+ },
902
+ revision: 'revision-3',
903
+ }
904
+ settings.setValues({
905
+ threshold: 3,
906
+ allowed_experiment_types: ['proteomics'],
907
+ })
908
+ const savedAgain = await settings.save()
909
+
910
+ expect({
911
+ saved,
912
+ savedAgain,
913
+ methods: requestMethods,
914
+ bodies: requestBodies,
915
+ writeHeaders: requestConfigs.slice(1).map(config => config.headers),
916
+ values: settings.values.value,
917
+ isDirty: settings.isDirty.value,
918
+ }).toEqual({
919
+ saved: true,
920
+ savedAgain: true,
921
+ methods: ['get', 'patch', 'patch', 'patch'],
922
+ bodies: [
923
+ { config: { threshold: 2 } },
924
+ { config: { allowed_experiment_types: ['proteomics'] } },
925
+ { config: { threshold: 3 } },
926
+ ],
927
+ writeHeaders: [undefined, undefined, undefined],
928
+ values: {
929
+ threshold: 3,
930
+ allowed_experiment_types: ['proteomics'],
931
+ },
932
+ isDirty: false,
933
+ })
934
+ })
935
+
936
+ it('should avoid a typed PUT when only the platform-owned setting changes', async () => {
937
+ ;(window as unknown as { __MINT_PLATFORM__?: unknown }).__MINT_PLATFORM__ = {
938
+ isIntegrated: true,
939
+ theme: 'light',
940
+ platformApiUrl: '/api',
941
+ plugin: {
942
+ id: 'drp',
943
+ name: 'drp',
944
+ version: '1.0.0',
945
+ route_prefix: '/drp',
946
+ api_prefix: '/api/drp',
947
+ },
948
+ }
949
+ nextGetData = {
950
+ plugin_name: 'drp',
951
+ config: {
952
+ threshold: 1,
953
+ allowed_experiment_types: ['metabolomics'],
954
+ },
955
+ revision: 'revision-1',
956
+ }
957
+ nextPatchData = {
958
+ plugin_name: 'drp',
959
+ config: {
960
+ threshold: 1,
961
+ allowed_experiment_types: ['proteomics'],
962
+ },
963
+ }
964
+
965
+ const settings = usePluginSettings<{ threshold?: number }>({ pluginName: 'drp' })
966
+ await settings.load()
967
+ settings.setValues({
968
+ threshold: 1,
969
+ allowed_experiment_types: ['proteomics'],
970
+ })
971
+
972
+ const saved = await settings.save()
973
+
974
+ expect({
975
+ saved,
976
+ methods: requestMethods,
977
+ bodies: requestBodies,
978
+ values: settings.values.value,
979
+ }).toEqual({
980
+ saved: true,
981
+ methods: ['get', 'patch'],
982
+ bodies: [{ config: { allowed_experiment_types: ['proteomics'] } }],
983
+ values: {
984
+ threshold: 1,
985
+ allowed_experiment_types: ['proteomics'],
986
+ },
987
+ })
988
+ })
989
+
990
+ it('should detect nested settings edits and restore the saved JSON snapshot', async () => {
991
+ nextGetData = {
992
+ settings: {
993
+ rules: {
994
+ thresholds: [1, 2],
995
+ },
996
+ },
997
+ mode: 'standalone',
998
+ }
999
+
1000
+ const settings = usePluginSettings<{
1001
+ rules?: { thresholds: number[] }
1002
+ }>({
1003
+ apiBaseUrl: '/api/drp',
1004
+ loadOnMount: false,
1005
+ })
1006
+ await settings.load()
1007
+ settings.values.value.rules!.thresholds[0] = 9
1008
+
1009
+ const dirtyAfterMutation = settings.isDirty.value
1010
+ settings.reset()
1011
+
1012
+ expect({
1013
+ dirtyAfterMutation,
1014
+ values: settings.values.value,
1015
+ isDirty: settings.isDirty.value,
1016
+ }).toEqual({
1017
+ dirtyAfterMutation: true,
1018
+ values: {
1019
+ rules: {
1020
+ thresholds: [1, 2],
1021
+ },
1022
+ },
1023
+ isDirty: false,
1024
+ })
725
1025
  })
726
1026
 
727
1027
  it('loads and saves plugin settings through the standalone settings router', async () => {
728
- nextGetData = { settings: { nas_base_path: '/data' }, mode: 'standalone' }
729
- nextPutData = { settings: { nas_base_path: '/mnt' }, mode: 'standalone' }
1028
+ nextGetData = {
1029
+ settings: { nas_base_path: '/data' },
1030
+ mode: 'standalone',
1031
+ revision: 'revision-1',
1032
+ }
1033
+ nextPutData = {
1034
+ settings: { nas_base_path: '/mnt' },
1035
+ mode: 'standalone',
1036
+ revision: 'revision-2',
1037
+ }
730
1038
 
731
1039
  const settings = usePluginSettings<{ nas_base_path?: string }>({
732
1040
  apiBaseUrl: '/api/drp',
@@ -740,6 +1048,7 @@ describe('usePluginClient', () => {
740
1048
  expect(requestConfigs[0]!.url).toBe('/settings')
741
1049
  expect(requestConfigs[1]!.baseURL).toBe('/api/drp')
742
1050
  expect(requestConfigs[1]!.url).toBe('/settings')
1051
+ expect(requestConfigs[1]!.headers).toEqual({ 'If-Match': '"revision-1"' })
743
1052
  expect(requestBodies[0]).toEqual({ nas_base_path: '/mnt' })
744
1053
  expect(settings.settingsConfig.value.values).toEqual({ nas_base_path: '/mnt' })
745
1054
  })