flowtask 5.8.4__cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
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.
- flowtask/__init__.py +93 -0
- flowtask/__main__.py +38 -0
- flowtask/bots/__init__.py +6 -0
- flowtask/bots/check.py +93 -0
- flowtask/bots/codebot.py +51 -0
- flowtask/components/ASPX.py +148 -0
- flowtask/components/AddDataset.py +352 -0
- flowtask/components/Amazon.py +523 -0
- flowtask/components/AutoTask.py +314 -0
- flowtask/components/Azure.py +80 -0
- flowtask/components/AzureUsers.py +106 -0
- flowtask/components/BaseAction.py +91 -0
- flowtask/components/BaseLoop.py +198 -0
- flowtask/components/BestBuy.py +800 -0
- flowtask/components/CSVToGCS.py +120 -0
- flowtask/components/CompanyScraper/__init__.py +1 -0
- flowtask/components/CompanyScraper/parsers/__init__.py +6 -0
- flowtask/components/CompanyScraper/parsers/base.py +102 -0
- flowtask/components/CompanyScraper/parsers/explorium.py +192 -0
- flowtask/components/CompanyScraper/parsers/leadiq.py +206 -0
- flowtask/components/CompanyScraper/parsers/rocket.py +133 -0
- flowtask/components/CompanyScraper/parsers/siccode.py +109 -0
- flowtask/components/CompanyScraper/parsers/visualvisitor.py +130 -0
- flowtask/components/CompanyScraper/parsers/zoominfo.py +118 -0
- flowtask/components/CompanyScraper/scrapper.py +1054 -0
- flowtask/components/CopyTo.py +177 -0
- flowtask/components/CopyToBigQuery.py +243 -0
- flowtask/components/CopyToMongoDB.py +291 -0
- flowtask/components/CopyToPg.py +609 -0
- flowtask/components/CopyToRethink.py +207 -0
- flowtask/components/CreateGCSBucket.py +102 -0
- flowtask/components/CreateReport/CreateReport.py +228 -0
- flowtask/components/CreateReport/__init__.py +9 -0
- flowtask/components/CreateReport/charts/__init__.py +15 -0
- flowtask/components/CreateReport/charts/bar.py +51 -0
- flowtask/components/CreateReport/charts/base.py +66 -0
- flowtask/components/CreateReport/charts/pie.py +64 -0
- flowtask/components/CreateReport/utils.py +9 -0
- flowtask/components/CustomerSatisfaction.py +196 -0
- flowtask/components/DataInput.py +200 -0
- flowtask/components/DateList.py +255 -0
- flowtask/components/DbClient.py +163 -0
- flowtask/components/DialPad.py +146 -0
- flowtask/components/DocumentDBQuery.py +200 -0
- flowtask/components/DownloadFrom.py +371 -0
- flowtask/components/DownloadFromD2L.py +113 -0
- flowtask/components/DownloadFromFTP.py +181 -0
- flowtask/components/DownloadFromIMAP.py +315 -0
- flowtask/components/DownloadFromS3.py +198 -0
- flowtask/components/DownloadFromSFTP.py +265 -0
- flowtask/components/DownloadFromSharepoint.py +110 -0
- flowtask/components/DownloadFromSmartSheet.py +114 -0
- flowtask/components/DownloadS3File.py +229 -0
- flowtask/components/Dummy.py +59 -0
- flowtask/components/DuplicatePhoto.py +411 -0
- flowtask/components/EmployeeEvaluation.py +237 -0
- flowtask/components/ExecuteSQL.py +323 -0
- flowtask/components/ExtractHTML.py +178 -0
- flowtask/components/FileBase.py +178 -0
- flowtask/components/FileCopy.py +181 -0
- flowtask/components/FileDelete.py +82 -0
- flowtask/components/FileExists.py +146 -0
- flowtask/components/FileIteratorDelete.py +112 -0
- flowtask/components/FileList.py +194 -0
- flowtask/components/FileOpen.py +75 -0
- flowtask/components/FileRead.py +120 -0
- flowtask/components/FileRename.py +106 -0
- flowtask/components/FilterIf.py +284 -0
- flowtask/components/FilterRows/FilterRows.py +200 -0
- flowtask/components/FilterRows/__init__.py +10 -0
- flowtask/components/FilterRows/functions.py +4 -0
- flowtask/components/GCSToBigQuery.py +103 -0
- flowtask/components/GoogleA4.py +150 -0
- flowtask/components/GoogleGeoCoding.py +344 -0
- flowtask/components/GooglePlaces.py +315 -0
- flowtask/components/GoogleSearch.py +539 -0
- flowtask/components/HTTPClient.py +268 -0
- flowtask/components/ICIMS.py +146 -0
- flowtask/components/IF.py +179 -0
- flowtask/components/IcimsFolderCopy.py +173 -0
- flowtask/components/ImageFeatures/__init__.py +5 -0
- flowtask/components/ImageFeatures/process.py +233 -0
- flowtask/components/IteratorBase.py +251 -0
- flowtask/components/LangchainLoader/__init__.py +5 -0
- flowtask/components/LangchainLoader/loader.py +194 -0
- flowtask/components/LangchainLoader/loaders/__init__.py +22 -0
- flowtask/components/LangchainLoader/loaders/abstract.py +362 -0
- flowtask/components/LangchainLoader/loaders/basepdf.py +50 -0
- flowtask/components/LangchainLoader/loaders/docx.py +91 -0
- flowtask/components/LangchainLoader/loaders/html.py +119 -0
- flowtask/components/LangchainLoader/loaders/pdfblocks.py +146 -0
- flowtask/components/LangchainLoader/loaders/pdfmark.py +79 -0
- flowtask/components/LangchainLoader/loaders/pdftables.py +135 -0
- flowtask/components/LangchainLoader/loaders/qa.py +67 -0
- flowtask/components/LangchainLoader/loaders/txt.py +55 -0
- flowtask/components/LeadIQ.py +650 -0
- flowtask/components/Loop.py +253 -0
- flowtask/components/Lowes.py +334 -0
- flowtask/components/MS365Usage.py +156 -0
- flowtask/components/MSTeamsMessages.py +320 -0
- flowtask/components/MarketClustering.py +1051 -0
- flowtask/components/MergeFiles.py +362 -0
- flowtask/components/MilvusOutput.py +87 -0
- flowtask/components/NearByStores.py +175 -0
- flowtask/components/NetworkNinja/__init__.py +6 -0
- flowtask/components/NetworkNinja/models/__init__.py +52 -0
- flowtask/components/NetworkNinja/models/abstract.py +177 -0
- flowtask/components/NetworkNinja/models/account.py +39 -0
- flowtask/components/NetworkNinja/models/client.py +19 -0
- flowtask/components/NetworkNinja/models/district.py +14 -0
- flowtask/components/NetworkNinja/models/events.py +101 -0
- flowtask/components/NetworkNinja/models/forms.py +499 -0
- flowtask/components/NetworkNinja/models/market.py +16 -0
- flowtask/components/NetworkNinja/models/organization.py +34 -0
- flowtask/components/NetworkNinja/models/photos.py +125 -0
- flowtask/components/NetworkNinja/models/project.py +44 -0
- flowtask/components/NetworkNinja/models/region.py +28 -0
- flowtask/components/NetworkNinja/models/store.py +203 -0
- flowtask/components/NetworkNinja/models/user.py +151 -0
- flowtask/components/NetworkNinja/router.py +854 -0
- flowtask/components/Odoo.py +175 -0
- flowtask/components/OdooInjector.py +192 -0
- flowtask/components/OpenFromXML.py +126 -0
- flowtask/components/OpenWeather.py +41 -0
- flowtask/components/OpenWithBase.py +616 -0
- flowtask/components/OpenWithPandas.py +715 -0
- flowtask/components/PGPDecrypt.py +199 -0
- flowtask/components/PandasIterator.py +187 -0
- flowtask/components/PandasToFile.py +189 -0
- flowtask/components/Paradox.py +339 -0
- flowtask/components/ParamIterator.py +117 -0
- flowtask/components/ParseHTML.py +84 -0
- flowtask/components/PlacerStores.py +249 -0
- flowtask/components/Pokemon.py +507 -0
- flowtask/components/PositiveBot.py +62 -0
- flowtask/components/PowerPointSlide.py +400 -0
- flowtask/components/PrintMessage.py +127 -0
- flowtask/components/ProductCompetitors/__init__.py +5 -0
- flowtask/components/ProductCompetitors/parsers/__init__.py +7 -0
- flowtask/components/ProductCompetitors/parsers/base.py +72 -0
- flowtask/components/ProductCompetitors/parsers/bestbuy.py +86 -0
- flowtask/components/ProductCompetitors/parsers/lowes.py +103 -0
- flowtask/components/ProductCompetitors/scrapper.py +155 -0
- flowtask/components/ProductCompliant.py +169 -0
- flowtask/components/ProductInfo/__init__.py +1 -0
- flowtask/components/ProductInfo/parsers/__init__.py +5 -0
- flowtask/components/ProductInfo/parsers/base.py +83 -0
- flowtask/components/ProductInfo/parsers/brother.py +97 -0
- flowtask/components/ProductInfo/parsers/canon.py +167 -0
- flowtask/components/ProductInfo/parsers/epson.py +118 -0
- flowtask/components/ProductInfo/parsers/hp.py +131 -0
- flowtask/components/ProductInfo/parsers/samsung.py +97 -0
- flowtask/components/ProductInfo/scraper.py +319 -0
- flowtask/components/ProductPricing.py +118 -0
- flowtask/components/QS.py +261 -0
- flowtask/components/QSBase.py +201 -0
- flowtask/components/QueryIterator.py +273 -0
- flowtask/components/QueryToInsert.py +327 -0
- flowtask/components/QueryToPandas.py +432 -0
- flowtask/components/RESTClient.py +195 -0
- flowtask/components/RethinkDBQuery.py +189 -0
- flowtask/components/Rsync.py +74 -0
- flowtask/components/RunSSH.py +59 -0
- flowtask/components/RunShell.py +71 -0
- flowtask/components/SalesForce.py +20 -0
- flowtask/components/SaveImageBank/__init__.py +257 -0
- flowtask/components/SchedulingVisits.py +592 -0
- flowtask/components/ScrapPage.py +216 -0
- flowtask/components/ScrapSearch.py +79 -0
- flowtask/components/SendNotify.py +257 -0
- flowtask/components/SentimentAnalysis.py +694 -0
- flowtask/components/ServiceScrapper/__init__.py +5 -0
- flowtask/components/ServiceScrapper/parsers/__init__.py +1 -0
- flowtask/components/ServiceScrapper/parsers/base.py +94 -0
- flowtask/components/ServiceScrapper/parsers/costco.py +93 -0
- flowtask/components/ServiceScrapper/scrapper.py +199 -0
- flowtask/components/SetVariables.py +156 -0
- flowtask/components/SubTask.py +182 -0
- flowtask/components/SuiteCRM.py +48 -0
- flowtask/components/Switch.py +175 -0
- flowtask/components/TableBase.py +148 -0
- flowtask/components/TableDelete.py +312 -0
- flowtask/components/TableInput.py +143 -0
- flowtask/components/TableOutput/TableOutput.py +384 -0
- flowtask/components/TableOutput/__init__.py +3 -0
- flowtask/components/TableSchema.py +534 -0
- flowtask/components/Target.py +223 -0
- flowtask/components/ThumbnailGenerator.py +156 -0
- flowtask/components/ToPandas.py +67 -0
- flowtask/components/TransformRows/TransformRows.py +507 -0
- flowtask/components/TransformRows/__init__.py +9 -0
- flowtask/components/TransformRows/functions.py +559 -0
- flowtask/components/TransposeRows.py +176 -0
- flowtask/components/UPCDatabase.py +86 -0
- flowtask/components/UnGzip.py +171 -0
- flowtask/components/Uncompress.py +172 -0
- flowtask/components/UniqueRows.py +126 -0
- flowtask/components/Unzip.py +107 -0
- flowtask/components/UpdateOperationalVars.py +147 -0
- flowtask/components/UploadTo.py +299 -0
- flowtask/components/UploadToS3.py +136 -0
- flowtask/components/UploadToSFTP.py +160 -0
- flowtask/components/UploadToSharepoint.py +205 -0
- flowtask/components/UserFunc.py +122 -0
- flowtask/components/VivaTracker.py +140 -0
- flowtask/components/WSDLClient.py +123 -0
- flowtask/components/Wait.py +18 -0
- flowtask/components/Walmart.py +199 -0
- flowtask/components/Workplace.py +134 -0
- flowtask/components/XMLToPandas.py +267 -0
- flowtask/components/Zammad/__init__.py +41 -0
- flowtask/components/Zammad/models.py +0 -0
- flowtask/components/ZoomInfoScraper.py +409 -0
- flowtask/components/__init__.py +104 -0
- flowtask/components/abstract.py +18 -0
- flowtask/components/flow.py +530 -0
- flowtask/components/google.py +335 -0
- flowtask/components/group.py +221 -0
- flowtask/components/py.typed +0 -0
- flowtask/components/reviewscrap.py +132 -0
- flowtask/components/tAutoincrement.py +117 -0
- flowtask/components/tConcat.py +109 -0
- flowtask/components/tExplode.py +119 -0
- flowtask/components/tFilter.py +184 -0
- flowtask/components/tGroup.py +236 -0
- flowtask/components/tJoin.py +270 -0
- flowtask/components/tMap/__init__.py +9 -0
- flowtask/components/tMap/functions.py +54 -0
- flowtask/components/tMap/tMap.py +450 -0
- flowtask/components/tMelt.py +112 -0
- flowtask/components/tMerge.py +114 -0
- flowtask/components/tOrder.py +93 -0
- flowtask/components/tPandas.py +94 -0
- flowtask/components/tPivot.py +71 -0
- flowtask/components/tPluckCols.py +76 -0
- flowtask/components/tUnnest.py +82 -0
- flowtask/components/user.py +401 -0
- flowtask/conf.py +457 -0
- flowtask/download.py +102 -0
- flowtask/events/__init__.py +11 -0
- flowtask/events/events/__init__.py +20 -0
- flowtask/events/events/abstract.py +95 -0
- flowtask/events/events/alerts/__init__.py +362 -0
- flowtask/events/events/alerts/colfunctions.py +131 -0
- flowtask/events/events/alerts/functions.py +158 -0
- flowtask/events/events/dummy.py +12 -0
- flowtask/events/events/exec.py +124 -0
- flowtask/events/events/file/__init__.py +7 -0
- flowtask/events/events/file/base.py +51 -0
- flowtask/events/events/file/copy.py +23 -0
- flowtask/events/events/file/delete.py +16 -0
- flowtask/events/events/interfaces/__init__.py +9 -0
- flowtask/events/events/interfaces/client.py +67 -0
- flowtask/events/events/interfaces/credentials.py +28 -0
- flowtask/events/events/interfaces/notifications.py +58 -0
- flowtask/events/events/jira.py +122 -0
- flowtask/events/events/log.py +26 -0
- flowtask/events/events/logerr.py +52 -0
- flowtask/events/events/notify.py +59 -0
- flowtask/events/events/notify_event.py +160 -0
- flowtask/events/events/publish.py +54 -0
- flowtask/events/events/sendfile.py +104 -0
- flowtask/events/events/task.py +97 -0
- flowtask/events/events/teams.py +98 -0
- flowtask/events/events/webhook.py +58 -0
- flowtask/events/manager.py +287 -0
- flowtask/exceptions.c +39393 -0
- flowtask/exceptions.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/extensions/__init__.py +3 -0
- flowtask/extensions/abstract.py +82 -0
- flowtask/extensions/logging/__init__.py +65 -0
- flowtask/hooks/__init__.py +9 -0
- flowtask/hooks/actions/__init__.py +22 -0
- flowtask/hooks/actions/abstract.py +66 -0
- flowtask/hooks/actions/dummy.py +23 -0
- flowtask/hooks/actions/jira.py +74 -0
- flowtask/hooks/actions/rest.py +320 -0
- flowtask/hooks/actions/sampledata.py +37 -0
- flowtask/hooks/actions/sensor.py +23 -0
- flowtask/hooks/actions/task.py +9 -0
- flowtask/hooks/actions/ticket.py +37 -0
- flowtask/hooks/actions/zammad.py +55 -0
- flowtask/hooks/hook.py +62 -0
- flowtask/hooks/models.py +17 -0
- flowtask/hooks/service.py +187 -0
- flowtask/hooks/step.py +91 -0
- flowtask/hooks/types/__init__.py +23 -0
- flowtask/hooks/types/base.py +129 -0
- flowtask/hooks/types/brokers/__init__.py +11 -0
- flowtask/hooks/types/brokers/base.py +54 -0
- flowtask/hooks/types/brokers/mqtt.py +35 -0
- flowtask/hooks/types/brokers/rabbitmq.py +82 -0
- flowtask/hooks/types/brokers/redis.py +83 -0
- flowtask/hooks/types/brokers/sqs.py +44 -0
- flowtask/hooks/types/fs.py +232 -0
- flowtask/hooks/types/http.py +49 -0
- flowtask/hooks/types/imap.py +200 -0
- flowtask/hooks/types/jira.py +279 -0
- flowtask/hooks/types/mail.py +205 -0
- flowtask/hooks/types/postgres.py +98 -0
- flowtask/hooks/types/responses/__init__.py +8 -0
- flowtask/hooks/types/responses/base.py +5 -0
- flowtask/hooks/types/sharepoint.py +288 -0
- flowtask/hooks/types/ssh.py +141 -0
- flowtask/hooks/types/tagged.py +59 -0
- flowtask/hooks/types/upload.py +85 -0
- flowtask/hooks/types/watch.py +71 -0
- flowtask/hooks/types/web.py +36 -0
- flowtask/interfaces/AzureClient.py +137 -0
- flowtask/interfaces/AzureGraph.py +839 -0
- flowtask/interfaces/Boto3Client.py +326 -0
- flowtask/interfaces/DropboxClient.py +173 -0
- flowtask/interfaces/ExcelHandler.py +94 -0
- flowtask/interfaces/FTPClient.py +131 -0
- flowtask/interfaces/GoogleCalendar.py +201 -0
- flowtask/interfaces/GoogleClient.py +133 -0
- flowtask/interfaces/GoogleDrive.py +127 -0
- flowtask/interfaces/GoogleGCS.py +89 -0
- flowtask/interfaces/GoogleGeocoding.py +93 -0
- flowtask/interfaces/GoogleLang.py +114 -0
- flowtask/interfaces/GooglePub.py +61 -0
- flowtask/interfaces/GoogleSheet.py +68 -0
- flowtask/interfaces/IMAPClient.py +137 -0
- flowtask/interfaces/O365Calendar.py +113 -0
- flowtask/interfaces/O365Client.py +220 -0
- flowtask/interfaces/OneDrive.py +284 -0
- flowtask/interfaces/Outlook.py +155 -0
- flowtask/interfaces/ParrotBot.py +130 -0
- flowtask/interfaces/SSHClient.py +378 -0
- flowtask/interfaces/Sharepoint.py +496 -0
- flowtask/interfaces/__init__.py +36 -0
- flowtask/interfaces/azureauth.py +119 -0
- flowtask/interfaces/cache.py +201 -0
- flowtask/interfaces/client.py +82 -0
- flowtask/interfaces/compress.py +525 -0
- flowtask/interfaces/credentials.py +124 -0
- flowtask/interfaces/d2l.py +239 -0
- flowtask/interfaces/databases/__init__.py +5 -0
- flowtask/interfaces/databases/db.py +223 -0
- flowtask/interfaces/databases/documentdb.py +55 -0
- flowtask/interfaces/databases/rethink.py +39 -0
- flowtask/interfaces/dataframes/__init__.py +11 -0
- flowtask/interfaces/dataframes/abstract.py +21 -0
- flowtask/interfaces/dataframes/arrow.py +71 -0
- flowtask/interfaces/dataframes/dt.py +69 -0
- flowtask/interfaces/dataframes/pandas.py +167 -0
- flowtask/interfaces/dataframes/polars.py +60 -0
- flowtask/interfaces/db.py +263 -0
- flowtask/interfaces/env.py +46 -0
- flowtask/interfaces/func.py +137 -0
- flowtask/interfaces/http.py +1780 -0
- flowtask/interfaces/locale.py +40 -0
- flowtask/interfaces/log.py +75 -0
- flowtask/interfaces/mask.py +143 -0
- flowtask/interfaces/notification.py +154 -0
- flowtask/interfaces/playwright.py +339 -0
- flowtask/interfaces/powerpoint.py +368 -0
- flowtask/interfaces/py.typed +0 -0
- flowtask/interfaces/qs.py +376 -0
- flowtask/interfaces/result.py +87 -0
- flowtask/interfaces/selenium_service.py +779 -0
- flowtask/interfaces/smartsheet.py +154 -0
- flowtask/interfaces/stat.py +39 -0
- flowtask/interfaces/task.py +96 -0
- flowtask/interfaces/template.py +118 -0
- flowtask/interfaces/vectorstores/__init__.py +1 -0
- flowtask/interfaces/vectorstores/abstract.py +133 -0
- flowtask/interfaces/vectorstores/milvus.py +669 -0
- flowtask/interfaces/zammad.py +107 -0
- flowtask/models.py +193 -0
- flowtask/parsers/__init__.py +15 -0
- flowtask/parsers/_yaml.c +11978 -0
- flowtask/parsers/_yaml.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/argparser.py +235 -0
- flowtask/parsers/base.c +15155 -0
- flowtask/parsers/base.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/json.c +11968 -0
- flowtask/parsers/json.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/parsers/maps.py +49 -0
- flowtask/parsers/toml.c +11968 -0
- flowtask/parsers/toml.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/plugins/__init__.py +16 -0
- flowtask/plugins/components/__init__.py +0 -0
- flowtask/plugins/handler/__init__.py +45 -0
- flowtask/plugins/importer.py +31 -0
- flowtask/plugins/sources/__init__.py +0 -0
- flowtask/runner.py +283 -0
- flowtask/scheduler/__init__.py +9 -0
- flowtask/scheduler/functions.py +493 -0
- flowtask/scheduler/handlers/__init__.py +8 -0
- flowtask/scheduler/handlers/manager.py +504 -0
- flowtask/scheduler/handlers/models.py +58 -0
- flowtask/scheduler/handlers/service.py +72 -0
- flowtask/scheduler/notifications.py +65 -0
- flowtask/scheduler/scheduler.py +993 -0
- flowtask/services/__init__.py +0 -0
- flowtask/services/bots/__init__.py +0 -0
- flowtask/services/bots/telegram.py +264 -0
- flowtask/services/files/__init__.py +11 -0
- flowtask/services/files/manager.py +522 -0
- flowtask/services/files/model.py +37 -0
- flowtask/services/files/service.py +767 -0
- flowtask/services/jira/__init__.py +3 -0
- flowtask/services/jira/jira_actions.py +191 -0
- flowtask/services/tasks/__init__.py +13 -0
- flowtask/services/tasks/launcher.py +213 -0
- flowtask/services/tasks/manager.py +323 -0
- flowtask/services/tasks/service.py +275 -0
- flowtask/services/tasks/task_manager.py +376 -0
- flowtask/services/tasks/tasks.py +155 -0
- flowtask/storages/__init__.py +16 -0
- flowtask/storages/exceptions.py +12 -0
- flowtask/storages/files/__init__.py +8 -0
- flowtask/storages/files/abstract.py +29 -0
- flowtask/storages/files/filesystem.py +66 -0
- flowtask/storages/tasks/__init__.py +19 -0
- flowtask/storages/tasks/abstract.py +26 -0
- flowtask/storages/tasks/database.py +33 -0
- flowtask/storages/tasks/filesystem.py +108 -0
- flowtask/storages/tasks/github.py +119 -0
- flowtask/storages/tasks/memory.py +45 -0
- flowtask/storages/tasks/row.py +25 -0
- flowtask/tasks/__init__.py +0 -0
- flowtask/tasks/abstract.py +526 -0
- flowtask/tasks/command.py +118 -0
- flowtask/tasks/pile.py +486 -0
- flowtask/tasks/py.typed +0 -0
- flowtask/tasks/task.py +778 -0
- flowtask/template/__init__.py +161 -0
- flowtask/tests.py +257 -0
- flowtask/types/__init__.py +8 -0
- flowtask/types/typedefs.c +11347 -0
- flowtask/types/typedefs.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/__init__.py +24 -0
- flowtask/utils/constants.py +117 -0
- flowtask/utils/encoders.py +21 -0
- flowtask/utils/executor.py +112 -0
- flowtask/utils/functions.cpp +14280 -0
- flowtask/utils/functions.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/json.cpp +13349 -0
- flowtask/utils/json.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/mail.py +63 -0
- flowtask/utils/parseqs.c +13324 -0
- flowtask/utils/parserqs.cpython-39-x86_64-linux-gnu.so +0 -0
- flowtask/utils/stats.py +308 -0
- flowtask/utils/transformations.py +74 -0
- flowtask/utils/uv.py +12 -0
- flowtask/utils/validators.py +97 -0
- flowtask/version.py +11 -0
- flowtask-5.8.4.dist-info/LICENSE +201 -0
- flowtask-5.8.4.dist-info/METADATA +209 -0
- flowtask-5.8.4.dist-info/RECORD +470 -0
- flowtask-5.8.4.dist-info/WHEEL +6 -0
- flowtask-5.8.4.dist-info/entry_points.txt +3 -0
- flowtask-5.8.4.dist-info/top_level.txt +2 -0
- plugins/components/CreateQR.py +39 -0
- plugins/components/TestComponent.py +28 -0
- plugins/components/Use1.py +13 -0
- plugins/components/Workplace.py +117 -0
- plugins/components/__init__.py +3 -0
- plugins/sources/__init__.py +0 -0
- plugins/sources/get_populartimes.py +78 -0
- plugins/sources/google.py +150 -0
- plugins/sources/hubspot.py +679 -0
- plugins/sources/icims.py +679 -0
- plugins/sources/mobileinsight.py +501 -0
- plugins/sources/newrelic.py +262 -0
- plugins/sources/uap.py +268 -0
- plugins/sources/venu.py +244 -0
- plugins/sources/vocinity.py +314 -0
@@ -0,0 +1,496 @@
|
|
1
|
+
import os
|
2
|
+
from typing import List, Optional, Union
|
3
|
+
from collections.abc import Callable
|
4
|
+
from urllib.parse import quote
|
5
|
+
from pathlib import Path, PurePath
|
6
|
+
from datetime import datetime, timedelta
|
7
|
+
from urllib.parse import urlparse
|
8
|
+
import requests
|
9
|
+
from tqdm import tqdm # Progress bar library
|
10
|
+
import pandas as pd
|
11
|
+
from io import BytesIO
|
12
|
+
from office365.sharepoint.client_context import ClientContext
|
13
|
+
from office365.runtime.http.request_options import RequestOptions
|
14
|
+
from office365.sharepoint.files.file import File
|
15
|
+
from ..exceptions import FileError, FileNotFound
|
16
|
+
from .O365Client import O365Client
|
17
|
+
from ..conf import (
|
18
|
+
SHAREPOINT_APP_ID,
|
19
|
+
SHAREPOINT_APP_SECRET,
|
20
|
+
SHAREPOINT_TENANT_ID,
|
21
|
+
SHAREPOINT_TENANT_NAME
|
22
|
+
)
|
23
|
+
|
24
|
+
class SharepointClient(O365Client):
|
25
|
+
"""
|
26
|
+
Sharepoint Client.
|
27
|
+
|
28
|
+
Managing connections to MS Sharepoint Resources.
|
29
|
+
"""
|
30
|
+
def __init__(self, *args, **kwargs):
|
31
|
+
super().__init__(*args, **kwargs)
|
32
|
+
# Default credentials
|
33
|
+
self._default_tenant_id = SHAREPOINT_TENANT_ID
|
34
|
+
self._default_client_id = SHAREPOINT_APP_ID
|
35
|
+
self._default_client_secret = SHAREPOINT_APP_SECRET
|
36
|
+
self._default_tenant_name = SHAREPOINT_TENANT_NAME
|
37
|
+
|
38
|
+
def get_context(self, url: str, *args) -> Callable:
|
39
|
+
return ClientContext(url, *args)
|
40
|
+
|
41
|
+
async def __aenter__(self):
|
42
|
+
return self
|
43
|
+
|
44
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
45
|
+
await self.close()
|
46
|
+
return self
|
47
|
+
|
48
|
+
def _start_(self, **kwargs):
|
49
|
+
# processing URL:
|
50
|
+
site = f"sites/{self.site}/" if self.site is not None else ""
|
51
|
+
self.site_url = f"https://{self.tenant}.sharepoint.com"
|
52
|
+
self.url = f"{self.site_url}/{site}".rstrip('/') # Ensure no trailing slash
|
53
|
+
if hasattr(self, '_srcfiles'):
|
54
|
+
for file in self._srcfiles:
|
55
|
+
fd = file.get('directory')
|
56
|
+
if 'sites' in fd:
|
57
|
+
file['directory'] = f"{fd}"
|
58
|
+
else:
|
59
|
+
file["directory"] = f"/{site}{fd}"
|
60
|
+
return True
|
61
|
+
|
62
|
+
def download_excel_from_sharepoint(
|
63
|
+
self,
|
64
|
+
file_url: str,
|
65
|
+
destination: Path = None,
|
66
|
+
as_pandas: bool = False
|
67
|
+
):
|
68
|
+
try:
|
69
|
+
response = self.context.web.get_file_by_server_relative_url(file_url).download().execute_query()
|
70
|
+
content = response.content
|
71
|
+
if as_pandas:
|
72
|
+
bytes_buffer = BytesIO(content)
|
73
|
+
df = pd.read_excel(bytes_buffer)
|
74
|
+
return df
|
75
|
+
else:
|
76
|
+
with open(destination, "wb") as local_file:
|
77
|
+
local_file.write(content)
|
78
|
+
return str(destination)
|
79
|
+
except Exception as err:
|
80
|
+
self._logger.error(
|
81
|
+
f"Error downloading Excel file {file_url}: {err}"
|
82
|
+
)
|
83
|
+
raise FileError(
|
84
|
+
f"Error downloading Excel file {file_url}: {err}"
|
85
|
+
) from err
|
86
|
+
|
87
|
+
async def file_search(self) -> List:
|
88
|
+
destinations = []
|
89
|
+
try:
|
90
|
+
# Get the default document library and its drive
|
91
|
+
drive = self.context.web.get_default_document_library().drive
|
92
|
+
|
93
|
+
for file in self._srcfiles:
|
94
|
+
directory = file["directory"]
|
95
|
+
fname = file["filename"]
|
96
|
+
|
97
|
+
# Search for the file within the drive
|
98
|
+
items = drive.root.search(f"name:{fname}")
|
99
|
+
self.context.load(items)
|
100
|
+
self.context.execute_query()
|
101
|
+
|
102
|
+
# Filter results to ensure they are within the specified directory
|
103
|
+
paths_matched = [
|
104
|
+
item.serverRelativeUrl for item in items
|
105
|
+
if fname in item.name and directory in item.serverRelativeUrl
|
106
|
+
]
|
107
|
+
|
108
|
+
if len(paths_matched) == 0:
|
109
|
+
self._logger.error(
|
110
|
+
f"Error downloading File: Pattern not match {fname}"
|
111
|
+
)
|
112
|
+
raise FileError(
|
113
|
+
f"Error downloading File: Pattern not match {fname}"
|
114
|
+
)
|
115
|
+
else:
|
116
|
+
for path in paths_matched:
|
117
|
+
file = path[path.rfind("/") + 1: len(path)]
|
118
|
+
destination = "{}/{}".format(self.directory, file)
|
119
|
+
try:
|
120
|
+
with open(destination, "wb") as local_file:
|
121
|
+
self.context.web.get_file_by_server_relative_url(
|
122
|
+
path
|
123
|
+
).download(local_file).execute_query()
|
124
|
+
destinations.append(destination)
|
125
|
+
except Exception as err:
|
126
|
+
raise RuntimeError(
|
127
|
+
f"Sharepoint: Error downloading file {path}: {err}"
|
128
|
+
) from err
|
129
|
+
return destinations
|
130
|
+
|
131
|
+
except Exception as e:
|
132
|
+
print(e)
|
133
|
+
|
134
|
+
async def file_download(self) -> List:
|
135
|
+
destinations = []
|
136
|
+
for file in self._srcfiles:
|
137
|
+
directory = file.get('directory', self.directory)
|
138
|
+
fname = file.get('filename', self.filename)
|
139
|
+
if self.filename is None:
|
140
|
+
self.filename = fname
|
141
|
+
destination = self.directory.joinpath(fname)
|
142
|
+
else:
|
143
|
+
destination = self.filename if self.filename else fname
|
144
|
+
if not directory.endswith('/'):
|
145
|
+
directory += '/'
|
146
|
+
source = f"{directory}{fname}"
|
147
|
+
try:
|
148
|
+
self._logger.notice(
|
149
|
+
f"Sharepoint Download: {source}"
|
150
|
+
)
|
151
|
+
self.context.web.get_file_by_server_relative_url(
|
152
|
+
source
|
153
|
+
).get().execute_query()
|
154
|
+
with open(destination, "wb") as local_file:
|
155
|
+
self.context.web.get_file_by_server_relative_url(source).download(
|
156
|
+
local_file
|
157
|
+
).execute_query()
|
158
|
+
destinations.append(destination)
|
159
|
+
except Exception as err:
|
160
|
+
print('Sharepoint ERROR > ', err)
|
161
|
+
if 'Not Found for url' in str(err):
|
162
|
+
raise FileNotFound(
|
163
|
+
f"File {fname} not found: {err}"
|
164
|
+
)
|
165
|
+
else:
|
166
|
+
self._logger.error(
|
167
|
+
f"Error downloading file {fname}: {err}"
|
168
|
+
)
|
169
|
+
raise FileError(
|
170
|
+
f"Error downloading file {fname}: {err}"
|
171
|
+
) from err
|
172
|
+
return destinations
|
173
|
+
|
174
|
+
async def download_files(self, files: List[dict], destination_dir: str) -> List:
|
175
|
+
"""
|
176
|
+
Download a list of files from SharePoint to a specified destination directory.
|
177
|
+
|
178
|
+
Args:
|
179
|
+
files (List[dict]): A list of dictionaries with 'directory' and 'filename' keys.
|
180
|
+
destination_dir (str): The local directory where files will be downloaded.
|
181
|
+
|
182
|
+
Returns:
|
183
|
+
List: A list of paths to the downloaded files.
|
184
|
+
"""
|
185
|
+
destination_dir = Path(destination_dir).resolve()
|
186
|
+
if not destination_dir.exists():
|
187
|
+
destination_dir.mkdir(parents=True, exist_ok=True)
|
188
|
+
|
189
|
+
destinations = []
|
190
|
+
|
191
|
+
for file in files:
|
192
|
+
directory = file.get('directory')
|
193
|
+
fname = file.get('filename')
|
194
|
+
|
195
|
+
if not directory or not fname:
|
196
|
+
raise ValueError(
|
197
|
+
"Each file entry must have both 'directory' and 'filename'."
|
198
|
+
)
|
199
|
+
|
200
|
+
# Build the SharePoint source path (directory + filename)
|
201
|
+
# Ensure forward slashes for SharePoint URLs
|
202
|
+
source = f"{directory}/{fname}".replace("\\", "/")
|
203
|
+
|
204
|
+
# Determine the destination file path
|
205
|
+
destination = destination_dir.joinpath(fname)
|
206
|
+
|
207
|
+
try:
|
208
|
+
# Fetch the file from SharePoint
|
209
|
+
self.context.web.get_file_by_server_relative_url(source).get().execute_query()
|
210
|
+
|
211
|
+
# Download the file to the local destination
|
212
|
+
with open(destination, "wb") as local_file:
|
213
|
+
self.context.web.get_file_by_server_relative_url(source).download(local_file).execute_query()
|
214
|
+
|
215
|
+
# Append the local destination path to the results
|
216
|
+
destinations.append(destination)
|
217
|
+
except Exception as err:
|
218
|
+
if 'Not Found for url' in str(err):
|
219
|
+
raise FileNotFound(
|
220
|
+
f"File {fname} not found: {err}"
|
221
|
+
)
|
222
|
+
else:
|
223
|
+
self._logger.error(
|
224
|
+
f"Error downloading file {fname}: {err}"
|
225
|
+
)
|
226
|
+
raise FileError(
|
227
|
+
f"Error downloading file {fname}: {err}"
|
228
|
+
) from err
|
229
|
+
|
230
|
+
return destinations
|
231
|
+
|
232
|
+
def print_upload_progress(self, offset):
|
233
|
+
file_size = os.path.getsize(str(self._file_handler))
|
234
|
+
print(
|
235
|
+
"Uploaded '{0}' bytes from '{1}'...[{2}%]".format(offset, file_size, round(offset / file_size * 100, 2))
|
236
|
+
)
|
237
|
+
|
238
|
+
def _update_progress_bar(self, progress_bar, offset):
|
239
|
+
"""Update the progress bar based on the current offset."""
|
240
|
+
progress_bar.n = offset # Set the current position
|
241
|
+
progress_bar.refresh() # Refresh the tqdm bar to show the update
|
242
|
+
|
243
|
+
async def _chunked_upload(
|
244
|
+
self,
|
245
|
+
target_folder: File,
|
246
|
+
file_path: Union[PurePath, Path],
|
247
|
+
file_name: str,
|
248
|
+
chunk_size: int = 10 * 1024 * 1024
|
249
|
+
) -> File:
|
250
|
+
"""
|
251
|
+
Perform a chunked upload for large files using the method from the GitHub example.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
target_folder: SharePoint folder where the file is to be uploaded.
|
255
|
+
file_path: Path of the local file.
|
256
|
+
file_name: Name of the file in SharePoint.
|
257
|
+
chunk_size: Size of each chunk in bytes (default is 10 MB).
|
258
|
+
|
259
|
+
Returns:
|
260
|
+
File: The uploaded file object in SharePoint.
|
261
|
+
"""
|
262
|
+
self._file_handler = file_path
|
263
|
+
file_size = os.path.getsize(file_path)
|
264
|
+
|
265
|
+
# Initialize tqdm progress bar
|
266
|
+
with tqdm(
|
267
|
+
total=file_size, unit='B', unit_scale=True, desc=f'Uploading {file_name}'
|
268
|
+
) as pbar:
|
269
|
+
with open(file_path, 'rb') as file:
|
270
|
+
uploaded_file: File = target_folder.files.create_upload_session(
|
271
|
+
file,
|
272
|
+
chunk_size,
|
273
|
+
lambda offset: self._update_progress_bar(pbar, offset),
|
274
|
+
file_name=file_name
|
275
|
+
).execute_query()
|
276
|
+
|
277
|
+
self._logger.debug(
|
278
|
+
'File {0} has been uploaded successfully'.format(uploaded_file.serverRelativeUrl)
|
279
|
+
)
|
280
|
+
|
281
|
+
server_relative_url = uploaded_file.serverRelativeUrl
|
282
|
+
encoded_path = quote(server_relative_url)
|
283
|
+
|
284
|
+
absolute_url = f"{self.site_url}{encoded_path}"
|
285
|
+
|
286
|
+
self._logger.debug(
|
287
|
+
f"File {absolute_url} has been uploaded successfully"
|
288
|
+
)
|
289
|
+
|
290
|
+
# Return the uploaded file object
|
291
|
+
return {
|
292
|
+
# "filename": uploaded_file,
|
293
|
+
"relative_url": server_relative_url,
|
294
|
+
"absolute_url": absolute_url
|
295
|
+
}
|
296
|
+
|
297
|
+
async def upload_files(self, filenames: Optional[List[Union[Path, PurePath]]] = None) -> List[dict]:
|
298
|
+
"""Upload files to a SharePoint folder using a resumable upload for large files."""
|
299
|
+
files = []
|
300
|
+
if not filenames:
|
301
|
+
filenames = self._srcfiles
|
302
|
+
|
303
|
+
for idx, file in enumerate(filenames):
|
304
|
+
destination_folder = self.directory # Destination SharePoint folder URL
|
305
|
+
try:
|
306
|
+
destination_file = self._destination[idx]
|
307
|
+
except KeyError:
|
308
|
+
destination_file = None
|
309
|
+
if isinstance(file, str):
|
310
|
+
file_path = Path(file) # Convert to Path object for compatibility
|
311
|
+
else:
|
312
|
+
file_path = file
|
313
|
+
file_size = file_path.stat().st_size
|
314
|
+
file_name = destination_file or file_path.name
|
315
|
+
|
316
|
+
try:
|
317
|
+
# Get the target folder
|
318
|
+
target_folder = self.context.web.get_folder_by_server_relative_url(destination_folder)
|
319
|
+
self.context.load(target_folder)
|
320
|
+
self.context.execute_query()
|
321
|
+
|
322
|
+
if file_size <= 4 * 1024 * 1024: # 4 MB threshold
|
323
|
+
# Small file, upload directly
|
324
|
+
async with open(file_path, "rb") as content_file:
|
325
|
+
file_content = await content_file.read()
|
326
|
+
target_file = target_folder.upload_file(file_name, file_content).execute_query()
|
327
|
+
else:
|
328
|
+
# Large file, use custom chunked upload
|
329
|
+
target_file = await self._chunked_upload(target_folder, file_path, file_name)
|
330
|
+
|
331
|
+
# Append file URL after successful upload
|
332
|
+
files.append(
|
333
|
+
{"filename": target_file}
|
334
|
+
)
|
335
|
+
|
336
|
+
except Exception as err:
|
337
|
+
self._logger.error(f"Error uploading file {file_name}: {err}")
|
338
|
+
raise FileError(f"Error uploading file {file_name}: {err}") from err
|
339
|
+
|
340
|
+
return files
|
341
|
+
|
342
|
+
async def upload_folder(self, local_folder: PurePath):
|
343
|
+
destinations = []
|
344
|
+
# destination:
|
345
|
+
destination = self._destination
|
346
|
+
try:
|
347
|
+
for p in local_folder.glob('**/*'):
|
348
|
+
# Check if it's a file or directory
|
349
|
+
if p.is_dir():
|
350
|
+
# Create corresponding folder in SharePoint if it doesn't exist
|
351
|
+
folder_path = self._get_sharepoint_folder_path(
|
352
|
+
p, local_folder, destination
|
353
|
+
)
|
354
|
+
await self._create_sharepoint_folder(folder_path)
|
355
|
+
elif p.is_file():
|
356
|
+
# Upload file to SharePoint
|
357
|
+
sharepoint_folder = self._get_sharepoint_folder_path(
|
358
|
+
p.parent, local_folder, destination
|
359
|
+
)
|
360
|
+
file_url = await self.upload_file(p, sharepoint_folder)
|
361
|
+
destinations.append(file_url)
|
362
|
+
return destinations # Return list of uploaded file URLs
|
363
|
+
|
364
|
+
except Exception as err:
|
365
|
+
self._logger.error(f"Error uploading folder {local_folder}: {err}")
|
366
|
+
raise FileError(f"Error uploading folder {local_folder}: {err}") from err
|
367
|
+
|
368
|
+
# Helper method to create folder paths in SharePoint
|
369
|
+
def _get_sharepoint_folder_path(
|
370
|
+
self,
|
371
|
+
path: PurePath,
|
372
|
+
local_folder: PurePath,
|
373
|
+
destination: str
|
374
|
+
) -> str:
|
375
|
+
"""Get the corresponding SharePoint folder path for a local file or folder."""
|
376
|
+
# Strip the local folder prefix and combine with SharePoint destination folder
|
377
|
+
relative_path = path.relative_to(local_folder)
|
378
|
+
sharepoint_folder_path = f"{destination}/{relative_path}".replace("\\", "/") # Convert to forward slashes
|
379
|
+
return sharepoint_folder_path
|
380
|
+
|
381
|
+
# Helper method to create a folder in SharePoint
|
382
|
+
async def _create_sharepoint_folder(
|
383
|
+
self,
|
384
|
+
folder_url: str
|
385
|
+
):
|
386
|
+
"""Create a folder in SharePoint if it doesn't exist."""
|
387
|
+
try:
|
388
|
+
folder = self.context.web.get_folder_by_server_relative_url(folder_url)
|
389
|
+
folder.execute_query() # Check if folder exists
|
390
|
+
except Exception:
|
391
|
+
# Folder does not exist, so create it
|
392
|
+
parent_folder_url = "/".join(folder_url.split("/")[:-1]) # Parent folder path
|
393
|
+
parent_folder = self.context.web.get_folder_by_server_relative_url(parent_folder_url)
|
394
|
+
parent_folder.folders.add(folder_url.split("/")[-1]).execute_query()
|
395
|
+
self._logger.info(f"Created folder: {folder_url}")
|
396
|
+
|
397
|
+
async def create_subscription(
|
398
|
+
self,
|
399
|
+
library_id: str,
|
400
|
+
webhook_url: str,
|
401
|
+
client_state: str = "secret_string",
|
402
|
+
expiration_days: int = 1
|
403
|
+
) -> dict:
|
404
|
+
"""
|
405
|
+
Create a webhook subscription to receive notifications when files are added, updated,
|
406
|
+
or deleted in a SharePoint document library.
|
407
|
+
|
408
|
+
Args:
|
409
|
+
library_id (str): The ID of the SharePoint document library to subscribe to.
|
410
|
+
webhook_url (str): The webhook URL to receive notifications.
|
411
|
+
client_state (str): A secret string to verify notifications.
|
412
|
+
expiration_days (int): Duration in days for the subscription to be valid (maximum is 180 days).
|
413
|
+
|
414
|
+
Returns:
|
415
|
+
dict: The response from Microsoft Graph API containing the subscription details.
|
416
|
+
"""
|
417
|
+
# Set up expiration for the subscription (max 180 days)
|
418
|
+
expiration_date = datetime.utcnow() + timedelta(days=expiration_days)
|
419
|
+
expiration_datetime = expiration_date.isoformat() + "Z"
|
420
|
+
|
421
|
+
# Define the subscription request body
|
422
|
+
request_body = {
|
423
|
+
"changeType": "created,updated,deleted",
|
424
|
+
"notificationUrl": webhook_url,
|
425
|
+
"resource": f"sites/{self.tenant}/lists/{library_id}", # Resource ID for the library
|
426
|
+
"expirationDateTime": expiration_datetime,
|
427
|
+
"clientState": client_state
|
428
|
+
}
|
429
|
+
|
430
|
+
# Acquire an access token for Microsoft Graph
|
431
|
+
access_token = self._access_token
|
432
|
+
headers = {
|
433
|
+
"Authorization": f"Bearer {access_token}",
|
434
|
+
"Content-Type": "application/json"
|
435
|
+
}
|
436
|
+
|
437
|
+
# Send the subscription request to Microsoft Graph API
|
438
|
+
url = "https://graph.microsoft.com/v1.0/subscriptions"
|
439
|
+
response = requests.post(url, headers=headers, json=request_body)
|
440
|
+
|
441
|
+
# Handle the response
|
442
|
+
if response.status_code == 201:
|
443
|
+
subscription_info = response.json()
|
444
|
+
print("Subscription created successfully:", subscription_info)
|
445
|
+
return subscription_info
|
446
|
+
else:
|
447
|
+
error_message = response.json().get("error", {}).get("message", "Unknown error")
|
448
|
+
print(f"Failed to create subscription: {error_message}")
|
449
|
+
raise RuntimeError(f"Failed to create subscription: {error_message}")
|
450
|
+
|
451
|
+
def get_library_id(self, absolute_url: str) -> str:
|
452
|
+
"""
|
453
|
+
Extracts the Library ID of a SharePoint document library from an absolute URL.
|
454
|
+
|
455
|
+
Args:
|
456
|
+
absolute_url (str): The absolute URL of the SharePoint resource.
|
457
|
+
|
458
|
+
Returns:
|
459
|
+
str: The ID of the document library.
|
460
|
+
|
461
|
+
Raises:
|
462
|
+
RuntimeError: If the library ID could not be retrieved.
|
463
|
+
"""
|
464
|
+
try:
|
465
|
+
# Parse the absolute URL to get site and document library path
|
466
|
+
parsed_url = urlparse(absolute_url)
|
467
|
+
path_parts = parsed_url.path.strip("/").split("/")
|
468
|
+
|
469
|
+
# Format the site name and library path
|
470
|
+
site_name = path_parts[1] # e.g., 'sites/mysite'
|
471
|
+
library_name = "/".join(path_parts[2:]) # e.g., 'Documents'
|
472
|
+
|
473
|
+
# Construct the Microsoft Graph API endpoint
|
474
|
+
graph_api_url = f"https://graph.microsoft.com/v1.0/sites/{self._default_tenant_name}:/{site_name}:/lists/{library_name}" # noqa
|
475
|
+
|
476
|
+
# Acquire access token
|
477
|
+
access_token = self._access_token
|
478
|
+
headers = {
|
479
|
+
"Authorization": f"Bearer {access_token}",
|
480
|
+
"Content-Type": "application/json"
|
481
|
+
}
|
482
|
+
|
483
|
+
# Make a GET request to retrieve the library details
|
484
|
+
response = requests.get(graph_api_url, headers=headers)
|
485
|
+
response.raise_for_status()
|
486
|
+
library_info = response.json()
|
487
|
+
|
488
|
+
# Extract and return the library ID
|
489
|
+
library_id = library_info.get("id")
|
490
|
+
if not library_id:
|
491
|
+
raise RuntimeError("Library ID could not be found in the response.")
|
492
|
+
print(f"Library ID for {absolute_url} is {library_id}")
|
493
|
+
return library_id
|
494
|
+
|
495
|
+
except Exception as err:
|
496
|
+
raise RuntimeError(f"Failed to retrieve library ID: {err}") from err
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
Interfaces.
|
3
|
+
|
4
|
+
Services and methods covered by Flowtask.
|
5
|
+
Support interfaces for many options on Task Components.
|
6
|
+
"""
|
7
|
+
from .func import FuncSupport
|
8
|
+
from .mask import MaskSupport
|
9
|
+
from .databases import DBSupport
|
10
|
+
from .log import LogSupport, SkipErrors
|
11
|
+
from .result import ResultSupport
|
12
|
+
from .cache import CacheSupport
|
13
|
+
from .stat import StatSupport
|
14
|
+
from .locale import LocaleSupport
|
15
|
+
from .template import TemplateSupport
|
16
|
+
from .http import HTTPService
|
17
|
+
from .selenium_service import SeleniumService
|
18
|
+
from .client import ClientInterface
|
19
|
+
from .db import DBInterface
|
20
|
+
|
21
|
+
|
22
|
+
__all__ = (
|
23
|
+
"FuncSupport",
|
24
|
+
"MaskSupport",
|
25
|
+
"DBSupport",
|
26
|
+
"LogSupport",
|
27
|
+
"ResultSupport",
|
28
|
+
"StatSupport",
|
29
|
+
"LocaleSupport",
|
30
|
+
"TemplateSupport",
|
31
|
+
"SkipErrors",
|
32
|
+
# interfaces:
|
33
|
+
"DBInterface",
|
34
|
+
"ClientInterface",
|
35
|
+
"HTTPService",
|
36
|
+
)
|
@@ -0,0 +1,119 @@
|
|
1
|
+
import msal
|
2
|
+
from navconfig.logging import logging
|
3
|
+
from ..conf import (
|
4
|
+
AZURE_TENANT_ID,
|
5
|
+
AZURE_CLIENT_ID,
|
6
|
+
AZURE_SECRET_ID,
|
7
|
+
)
|
8
|
+
"""
|
9
|
+
AzureAuth Class
|
10
|
+
|
11
|
+
Overview
|
12
|
+
|
13
|
+
This class facilitates Azure Active Directory (AAD) authentication using the Microsoft Authentication Library (MSAL) for Python. It retrieves access tokens for a user or a client application based on provided credentials or cached tokens.
|
14
|
+
|
15
|
+
.. table:: Properties
|
16
|
+
:widths: auto
|
17
|
+
|
18
|
+
+------------------------+----------+-----------+---------------------------------------------------------------------------------+
|
19
|
+
| Name | Required | Summary |
|
20
|
+
+------------------------+----------+-----------+---------------------------------------------------------------------------------+
|
21
|
+
| tenant_id (optional) | No | Azure tenant ID (defaults to value from settings.settings.AZURE_TENANT_ID). |
|
22
|
+
+------------------------+----------+-----------+---------------------------------------------------------------------------------+
|
23
|
+
| client_id (optional) | No | Azure client application ID (defaults to value from settings.settings.AZURE_CLIENT_ID). |
|
24
|
+
+------------------------+----------+-----------+---------------------------------------------------------------------------------+
|
25
|
+
| client_secret | No | Azure client application secret (defaults to value from settings.settings.AZURE_SECRET_ID). |
|
26
|
+
+------------------------+----------+-----------+---------------------------------------------------------------------------------+
|
27
|
+
| scopes (optional) | No | List of OAuth 2.0 scopes to request during token acquisition |
|
28
|
+
| | | (defaults to a list including "offline_access", "https://outlook.office365.com/.default", |
|
29
|
+
| | | "email", "openid", and "profile"). |
|
30
|
+
+------------------------+----------+-----------+---------------------------------------------------------------------------------+
|
31
|
+
|
32
|
+
Methods:
|
33
|
+
* get_msal_client(client: bool = True): Returns an MSAL ClientApplication or ConfidentialClientApplication instance based on the client argument.
|
34
|
+
* get_token(username: str = None, password: str = None) -> str: Acquires an access token using username/password or from cache if available. Raises an exception on failure.
|
35
|
+
* binary_token(username: str = None, password: str = None) -> str: Retrieves an access token and constructs a binary auth string with user and token information.
|
36
|
+
|
37
|
+
This class is likely used within other components or scripts to authenticate with Azure services.
|
38
|
+
|
39
|
+
""" # noqa
|
40
|
+
|
41
|
+
DEFAULT_SCOPES = [
|
42
|
+
"offline_access https://outlook.office365.com/.default email openid profile"
|
43
|
+
]
|
44
|
+
|
45
|
+
msal_logger = logging.getLogger("msal")
|
46
|
+
msal_logger.setLevel(logging.WARNING)
|
47
|
+
|
48
|
+
|
49
|
+
def generate_auth_string(user, token):
|
50
|
+
return f"user={user}\x01Auth=Bearer {token}\x01\x01"
|
51
|
+
|
52
|
+
|
53
|
+
class AzureAuth:
|
54
|
+
def get_msal_client(self, client: bool = True):
|
55
|
+
if client is True:
|
56
|
+
return msal.ClientApplication(
|
57
|
+
self.client_id,
|
58
|
+
authority=self.authority,
|
59
|
+
client_credential=self.client_secret,
|
60
|
+
validate_authority=True,
|
61
|
+
)
|
62
|
+
else:
|
63
|
+
return msal.ConfidentialClientApplication(
|
64
|
+
self.client_id,
|
65
|
+
authority=self.authority,
|
66
|
+
client_credential=self.client_secret,
|
67
|
+
validate_authority=True,
|
68
|
+
)
|
69
|
+
|
70
|
+
def __init__(
|
71
|
+
self,
|
72
|
+
tenant_id: str = None,
|
73
|
+
client_id: str = None,
|
74
|
+
client_secret: str = None,
|
75
|
+
scopes: list = None,
|
76
|
+
) -> None:
|
77
|
+
self.tenant_id = tenant_id if tenant_id else AZURE_TENANT_ID
|
78
|
+
self.authority = f"https://login.microsoftonline.com/{self.tenant_id}"
|
79
|
+
# credentials:
|
80
|
+
self.client_id = client_id if client_id else AZURE_CLIENT_ID
|
81
|
+
self.client_secret = client_secret if client_secret else AZURE_SECRET_ID
|
82
|
+
# Token URL
|
83
|
+
self.token_uri = (
|
84
|
+
f"https://login.microsoftonline.com/{self.tenant_id}/oauth2/v2.0/token"
|
85
|
+
)
|
86
|
+
# scopes:
|
87
|
+
self.scopes = scopes if scopes is not None else DEFAULT_SCOPES
|
88
|
+
|
89
|
+
def get_token(self, username: str = None, password: str = None) -> str:
|
90
|
+
result = None
|
91
|
+
if username is not None:
|
92
|
+
app = self.get_msal_client(client=True)
|
93
|
+
account = app.get_accounts(username=username)
|
94
|
+
if account:
|
95
|
+
logging.info("Account(s) exists in cache, probably with token too")
|
96
|
+
result = app.acquire_token_silent(self.scopes, account=account[0])
|
97
|
+
else:
|
98
|
+
result = app.acquire_token_by_username_password(
|
99
|
+
username, password, self.scopes
|
100
|
+
)
|
101
|
+
else:
|
102
|
+
app = self.get_msal_client(client=False)
|
103
|
+
result = app.acquire_token_silent(self.scopes, account=None)
|
104
|
+
if not result:
|
105
|
+
logging.info("No suitable token in cache. Get new one.")
|
106
|
+
result = app.acquire_token_for_client(scopes=self.scopes)
|
107
|
+
if "access_token" in result:
|
108
|
+
return result
|
109
|
+
else:
|
110
|
+
error = {
|
111
|
+
"error": result.get("error"),
|
112
|
+
"message": result.get("error_description"),
|
113
|
+
"correlation_id": result.get("correlation_id"),
|
114
|
+
}
|
115
|
+
raise Exception(f"Unable to Access: {error!s}")
|
116
|
+
|
117
|
+
def binary_token(self, username: str = None, password: str = None) -> str:
|
118
|
+
result = self.get_token(username=username, password=password)
|
119
|
+
return generate_auth_string(username, result["access_token"])
|