Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Filter by tags #2311

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 82 additions & 23 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@
"request": "launch",
"name": "Azurite Service - Loki",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/azurite.ts", "-d", "debug.log"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/src/azurite.ts",
"-d",
"debug.log"
],
"env": {
"AZURITE_ACCOUNTS": ""
},
"skipFiles": ["node_modules/*/**", "<node_internals>/*/**"],
"skipFiles": [
"node_modules/*/**",
"<node_internals>/*/**"
],
"outputCapture": "std"
},
{
Expand All @@ -35,8 +45,16 @@
"request": "launch",
"name": "Azurite Service - Loki, Loose",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/azurite.ts", "-d", "debug.log", "-L"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/src/azurite.ts",
"-d",
"debug.log",
"-L"
],
"env": {
"AZURITE_ACCOUNTS": ""
},
Expand All @@ -47,7 +65,10 @@
"request": "launch",
"name": "Azurite Service - Loki, Loose, HTTPS, OAuth",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/src/azurite.ts",
"-L",
Expand All @@ -70,7 +91,10 @@
"request": "launch",
"name": "Azurite Queue Service - Loki, HTTPS, OAuth",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/src/queue/main.ts",
"-d",
Expand All @@ -92,7 +116,10 @@
"request": "launch",
"name": "Azurite Blob Service - Loki, HTTPS, OAuth",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/src/blob/main.ts",
"-d",
Expand All @@ -114,8 +141,15 @@
"request": "launch",
"name": "Azurite Blob Service - SQL",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/blob/main.ts", "-d", "debug.log"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/src/blob/main.ts",
"-d",
"debug.log"
],
"env": {
"AZURITE_DB": "mysql://root:[email protected]:3306/azurite_blob",
"AZURITE_ACCOUNTS": ""
Expand All @@ -127,8 +161,15 @@
"request": "launch",
"name": "Azurite Table Service - Loki",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/table/main.ts", "-d", "debug.log"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/src/table/main.ts",
"-d",
"debug.log"
],
"env": {
"AZURITE_ACCOUNTS": ""
},
Expand All @@ -139,16 +180,24 @@
"request": "launch",
"name": "Current TS File",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/${relativeFile}"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/${relativeFile}"
],
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "Current Mocha TS File - Loki",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/node_modules/mocha/bin/_mocha",
"-u",
Expand All @@ -162,7 +211,7 @@
"AZURITE_ACCOUNTS": "",
"AZURE_TABLE_STORAGE": "",
"DATATABLES_ACCOUNT_NAME": "<name of your storage account>",
"DATATABLES_ACCOUNT_KEY" : "<account key for your storage account>",
"DATATABLES_ACCOUNT_KEY": "<account key for your storage account>",
"AZURE_DATATABLES_STORAGE_STRING": "https://<your account name>.table.core.windows.net",
"AZURE_DATATABLES_SAS": "?<sas query string>",
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
Expand All @@ -175,15 +224,18 @@
"request": "launch",
"name": "EXE Mocha TS File - Loki",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/node_modules/mocha/bin/_mocha",
"-u",
"tdd",
"--timeout",
"999999",
"--colors",
"${workspaceFolder}/tests/exe.test.ts",
"${workspaceFolder}/tests/exe.test.ts",
"--exit"
],
"env": {
Expand All @@ -199,7 +251,10 @@
"request": "launch",
"name": "Current Mocha TS File - SQL",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["-r", "ts-node/register"],
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"${workspaceFolder}/node_modules/mocha/bin/_mocha",
"-u",
Expand All @@ -211,7 +266,7 @@
],
"env": {
"AZURITE_ACCOUNTS": "",
"AZURITE_TEST_DB": "mysql://root:my-secret-pw@127.0.0.1:3306/azurite_blob_test",
"AZURITE_TEST_DB": "mysql://root:!!123abc@127.0.0.1:3306/azurite_blob",
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
},
"internalConsoleOptions": "openOnSessionStart",
Expand Down Expand Up @@ -250,9 +305,13 @@
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "npm: watch"
}
]
}
}
10 changes: 9 additions & 1 deletion src/blob/conditions/ConditionResourceAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { BlobModel, ContainerModel } from "../persistence/IBlobMetadataStore";
import { BlobModel, ContainerModel, FilterBlobModel } from "../persistence/IBlobMetadataStore";
import IConditionResource from "./IConditionResource";

export default class ConditionResourceAdapter implements IConditionResource {
public exist: boolean;
public etag: string;
public lastModified: Date;
public blobItemWithTags?: FilterBlobModel;

public constructor(resource: BlobModel | ContainerModel | undefined | null) {
if (
Expand Down Expand Up @@ -33,5 +34,12 @@ export default class ConditionResourceAdapter implements IConditionResource {

this.lastModified = new Date(resource.properties.lastModified);
this.lastModified.setMilliseconds(0); // Precision to seconds

const blobItem = resource as BlobModel;
this.blobItemWithTags = {
name: blobItem.name,
containerName: blobItem.containerName,
tags: blobItem.blobTags
};
}
}
3 changes: 3 additions & 0 deletions src/blob/conditions/ConditionalHeadersAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default class ConditionalHeadersAdapter implements IConditionalHeaders {
public ifUnmodifiedSince?: Date;
public ifMatch?: string[];
public ifNoneMatch?: string[];
public ifTags?: string;

public constructor(
context: Context,
Expand Down Expand Up @@ -43,5 +44,7 @@ export default class ConditionalHeadersAdapter implements IConditionalHeaders {
if (this.ifUnmodifiedSince) {
this.ifUnmodifiedSince.setMilliseconds(0); // Precision to seconds
}

this.ifTags = modifiedAccessConditions.ifTags;
}
}
3 changes: 3 additions & 0 deletions src/blob/conditions/IConditionResource.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FilterBlobModel } from "../persistence/IBlobMetadataStore";

export default interface IConditionResource {
/**
* Whether resource exists or not.
Expand All @@ -13,4 +15,5 @@ export default interface IConditionResource {
* last modified time for container or blob.
*/
lastModified: Date;
blobItemWithTags?: FilterBlobModel;
}
5 changes: 5 additions & 0 deletions src/blob/conditions/IConditionalHeaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ export interface IConditionalHeaders {
* If-None-Match etag list without quotes.
*/
ifNoneMatch?: string[];

/**
* Specify a SQL where clause on blob tags to operate only on blobs with a matching value.
*/
ifTags?: string;
}
3 changes: 2 additions & 1 deletion src/blob/conditions/IConditionalHeadersValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface IConditionalHeadersValidator {
validate(
context: Context,
conditionalHeaders: IConditionalHeaders,
resource: IConditionResource
resource: IConditionResource,
isSourceBlob?: boolean
): void;
}
23 changes: 19 additions & 4 deletions src/blob/conditions/ReadConditionalHeadersValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import StorageErrorFactory from "../errors/StorageErrorFactory";
import { ModifiedAccessConditions } from "../generated/artifacts/models";
import Context from "../generated/Context";
import { BlobModel, ContainerModel } from "../persistence/IBlobMetadataStore";
import { generateQueryBlobWithTagsWhereFunction } from "../persistence/QueryInterpreter/QueryInterpreter";
import ConditionalHeadersAdapter from "./ConditionalHeadersAdapter";
import ConditionResourceAdapter from "./ConditionResourceAdapter";
import { IConditionalHeaders } from "./IConditionalHeaders";
Expand All @@ -11,12 +12,14 @@ import IConditionResource from "./IConditionResource";
export function validateReadConditions(
context: Context,
conditionalHeaders?: ModifiedAccessConditions,
model?: BlobModel | ContainerModel | null
model?: BlobModel | ContainerModel | null,
isSourceBlob?: boolean
) {
new ReadConditionalHeadersValidator().validate(
context,
new ConditionalHeadersAdapter(context, conditionalHeaders),
new ConditionResourceAdapter(model)
new ConditionResourceAdapter(model),
isSourceBlob
);
}

Expand All @@ -30,11 +33,13 @@ export default class ReadConditionalHeadersValidator
* @param context
* @param conditionalHeaders
* @param resource
* @param isSourceBlob
*/
public validate(
context: Context,
conditionalHeaders: IConditionalHeaders,
resource: IConditionResource
resource: IConditionResource,
isSourceBlob?: boolean
): void {
// If-Match && If-Unmodified-Since && (If-None-Match || If-Modified-Since)

Expand Down Expand Up @@ -66,7 +71,7 @@ export default class ReadConditionalHeadersValidator
// If-Match
const ifMatchPass = conditionalHeaders.ifMatch
? conditionalHeaders.ifMatch.includes(resource.etag) ||
conditionalHeaders.ifMatch[0] === "*"
conditionalHeaders.ifMatch[0] === "*"
: undefined;

// If-Unmodified-Since
Expand Down Expand Up @@ -107,6 +112,16 @@ export default class ReadConditionalHeadersValidator
if (isModifiedSincePass === false && ifNoneMatchPass !== true) {
throw StorageErrorFactory.getNotModified(context.contextId!);
}

if (conditionalHeaders.ifTags) {
const againstSourceBlob = isSourceBlob === undefined ? false : isSourceBlob;
const validateFunction = generateQueryBlobWithTagsWhereFunction(context, conditionalHeaders.ifTags, againstSourceBlob ? 'x-ms-source-if-tags' : 'x-ms-if-tags');

if (conditionalHeaders?.ifTags !== undefined
&& validateFunction(resource.blobItemWithTags).length === 0) {
throw StorageErrorFactory.getConditionNotMet(context.contextId!);
}
}
}
}
}
Loading